Sitecore 10 Forms support of fallback language in Forms Builder

For one of the implementations I worked with, the customer was heavily using fallback languages. This was also the case regarding the forms created.




But after enabling the fallback languages at the Sitecore Forms templates (__Standard Values), the Form builder application start acting differently regarding the forms created in the main language but not translated. For each fallback language, Sitecore Forms stated that the language version was created: 



When we tried to select the new language and saved the version, the form item was marked as protected – and Sitecore Form application provided a notification saying:

“You cannot edit the [Name of the form] item because it is protected. To save your
changes, click Save as and save it as a new form.”

Not quite what we expected. 



It seemed like the Sitecore Form implementation didn’t handled the fallback language correctly. 

Step 1

The first step was to modify the implementation for how Sitecore resolves whether the language has been created for a specific language version. We just added a statement for “!item.IsFallback” in the Sitecore.ExperienceForms.Client.Pipelines.GetLanguages.GetLanguageData.ToLanguageListItem(). To check whether the item is marked with fallback language for the selected language version:

Check whether language version has been created:

using System;

using Sitecore.Data.Items;

using Sitecore.Diagnostics;

using Sitecore.ExperienceForms.Client.Models.Builder;

using Sitecore.ExperienceForms.Client.Pipelines.GetLanguages;

using Sitecore.Globalization;

 

namespace Norican.Infrastructure.Web.Sc.Forms.Pipelines

{

    public class GetLanguageDataWithItemFallback : GetLanguageData

    {

        protected override LanguageListItem ToLanguageListItem(Item currentItem, Language language)

        {

            Assert.ArgumentNotNull(language, "language");

            Item item = currentItem?.Database.GetItem(currentItem.ID, language);

            //enhanced standard Sitecore implementation by checking if form item is not language fallback

            bool flag = item != null && !item.IsFallback && item.Versions.Count > 0;

            LanguageListItem languageListItem = new LanguageListItem

            {

                Name = language.Name,

                DisplayName = language.CultureInfo.DisplayName,

                Text = FormattableString.Invariant(

                    $"{language.CultureInfo.DisplayName} : {language.CultureInfo.NativeName}"),

                Description = (flag ? Translate.Text("Created.") : Translate.Text("Not created.")),

                HasVersions = flag

            };

            return languageListItem;

        }

    }

}


Step 2

Next step was to handle the notification for creating a new version of the form item. Once again we simply added the check whether the form is marked with fallback language in the Sitecore.ExperienceForms.Client.Pipelines.GetImportantNotes.CheckFormIsEditable with !args.Item.IsFallback.

Check when for form is editable: 

using System;

using System.Globalization;

using Sitecore.Diagnostics;

using Sitecore.ExperienceForms.Client.Pipelines.GetImportantNotes;

using Sitecore.Globalization;

 

namespace Norican.Infrastructure.Web.Sc.Forms.Pipelines

{

    public class CheckFormIsEditableWithItemFallback : CheckFormIsEditable

    {

        public override void Process(ImportantNotesEventArgs args)

        {

            Assert.ArgumentNotNull((object)args, nameof(args));

            if (args.Item == null)

            {

                args.AddMessage(Translate.Text("Item does not exist."));

                args.AbortPipeline();

            }

            else

            {

                string str = "";

                if (args.Item.Parent.Access.CanCreate())

                    str = Translate.Text("To save your changes, click Save as and save it as a new form.");

                if (!args.Item.Access.CanWrite())

                    args.Result = (Translate.Text("You cannot edit the '{0}' item because you do not have write access.", (object)args.Item.DisplayName) + " " + str).ToString((IFormatProvider)CultureInfo.CurrentCulture);

                //enhanced standard Sitecore implementation by checking if form item is not language fallback

                else if (!args.Item.IsFallback && args.Item.Appearance.ReadOnly)

                {

                    args.Result = (Translate.Text("You cannot edit the '{0}' item because it is protected.", (object)args.Item.DisplayName) + " " + str).ToString((IFormatProvider)CultureInfo.CurrentCulture);

                }

                else

                {

                    if (Sitecore.Context.User.IsAdministrator || !args.Item.Locking.IsLocked() || args.Item.Locking.HasLock())

                        return;

                    args.Result = (Translate.Text("You cannot edit this item because '{0}' has locked it.", (object)args.Item.Locking.GetOwnerWithoutDomain()) + " " + str).ToString((IFormatProvider)CultureInfo.CurrentCulture);

                }

            }

        }

    }

}


Step 3

The last step was for saving the form item. Once again by adding a check for whether the form was marked with use of fallback language !obj2.IsFallback in the Sitecore.ExperienceForms.Client.Pipelines.SaveForm.CheckAccessRights:

Check when saving the forms:

using System;

using System.Globalization;

using System.Linq;

using Sitecore;

using Sitecore.Data;

using Sitecore.Data.Items;

using Sitecore.Diagnostics;

using Sitecore.ExperienceForms;

using Sitecore.ExperienceForms.Client.Models.Builder;

using Sitecore.ExperienceForms.Client.Pipelines.SaveForm;

using Sitecore.ExperienceForms.Mvc.Constants;

using Sitecore.Globalization;

 

namespace Norican.Infrastructure.Web.Sc.Forms.Pipelines

{

    public class CheckAccessRightsWithItemFallback : CheckAccessRights

    {

        public override void Process(SaveFormEventArgs args)

        {

            Assert.ArgumentNotNull((object)args, nameof(args));

            if (args.ViewModelWrappers == null)

            {

                args.AddMessage(Translate.Text("There is no data in the view model wrappers list."));

            }

            else

            {

                ViewModelWrapper viewModelWrapper = args.ViewModelWrappers.FirstOrDefault<ViewModelWrapper>((Func<ViewModelWrapper, bool>)(m => ID.Parse(m.Model.TemplateId) == TemplateIds.FormTemplateId));

                if (viewModelWrapper == null)

                {

                    args.AddMessage(Translate.Text("The form model was not found in the view model wrappers collection."));

                }

                else

                {

                    Item obj1 = args.FormBuilderContext.Database.GetItem(viewModelWrapper.ParentId, args.FormBuilderContext.Language);

                    if ((args.FormBuilderContext.FormBuilderMode == FormBuilderMode.New || args.FormBuilderContext.FormBuilderMode == FormBuilderMode.Copy) && !obj1.Access.CanCreate())

                    {

                        args.AddMessage(Translate.Text("You do not have permission to create items here."));

                        args.AbortPipeline();

                    }

                    else

                    {

                        string str = "";

                        if (obj1.Access.CanCreate())

                            str = " " + Translate.Text("To save your changes, click Save as and save it as a new form.");

                        if (args.FormBuilderContext.FormBuilderMode != FormBuilderMode.Edit)

                            return;

                        ID id = ID.Parse(viewModelWrapper.Model.ItemId);

                        if (!args.FormBuilderContext.Database.Items.Exists(id))

                            return;

                        Item obj2 = args.FormBuilderContext.Database.GetItem(id, args.FormBuilderContext.Language);

                        if (!obj2.Access.CanWrite())

                        {

                            args.AddMessage((Translate.Text("You cannot edit the '{0}' item because you do not have write access.", (object)obj2.DisplayName) + str).ToString((IFormatProvider)CultureInfo.CurrentCulture));

                            args.AbortPipeline();

                        }

                        //enhanced standard Sitecore implementation by checking if form item is not language fallback

                        else if (!obj2.IsFallback && obj2.Appearance.ReadOnly)

                        {

                            args.AddMessage((Translate.Text("You cannot edit the '{0}' item because it is protected.", (object)obj2.DisplayName) + str).ToString((IFormatProvider)CultureInfo.CurrentCulture));

                            args.AbortPipeline();

                        }

                        else

                        {

                            if (Context.User.IsAdministrator || !obj2.Locking.IsLocked() || obj2.Locking.HasLock())

                                return;

                            args.AddMessage((Translate.Text("You cannot edit this item because '{0}' has locked it.", (object)obj2.Locking.GetOwnerWithoutDomain()) + str).ToString((IFormatProvider)CultureInfo.CurrentCulture));

                            args.AbortPipeline();

                        }

                    }

                }

            }

        }

    }

}



After that, testing the language versions of the forms behaved as expected:




Kudos to my coll' Giedrius Gobe for the fix!











No comments: