Schedule XForm data mailings in MVC

In my last post I wrote about a way to schedule emails, instead of sending them immediately.

This was for WebForms though. Getting this to work in MVC was not that easy. I am not sure if there are some bugs in EPiServer, or that I am missing something somewhere, but this is what it took to get it to work.

A basic model

public class XFormPage : SitePageData
    {
        [Display(
            GroupName = SystemTabNames.Content,
            Order = 310)]
        [CultureSpecific]
        public virtual XhtmlString MainBody { get; set; }

        [Display(
            GroupName = SystemTabNames.Content,
            Order = 320)]
        [CultureSpecific]
        public virtual XForm Form { get; set; }

        [Display(
            GroupName = SystemTabNames.Content,
            Order = 330)]
        public virtual ContentArea MainContentArea { get; set; }
    }

The controller

public class XFormPageController : PageController<XFormPage>
    {
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Failed(XFormPage currentPage, XFormPostedData xFormPostedData)
        {
            PageViewModel<XFormPage> model = PageViewModel.Create(currentPage);
            return View("Failed", model);
        }

        public ActionResult Index(XFormPage currentPage)
        {
            PageViewModel<XFormPage> model = PageViewModel.Create(currentPage);
            return View(model);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Posted(XFormPage currentPage, XFormPostedData xFormpostedData)
        {
            XFormBeforeSubmitPostedData(xFormpostedData, currentPage.PageGuid);
            PageViewModel<XFormPage> model = PageViewModel.Create(currentPage);
            XFormData formData = new XFormPageHelper().GetXFormData(this, xFormpostedData);

            return View(XFormActionHelper.DoAction(formData, xFormpostedData, true) ? "Success" : "Failed", model);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Success(XFormPage currentPage, XFormPostedData xFormPostedData)
        {
            PageViewModel<XFormPage> model = PageViewModel.Create(currentPage);
            return View("Success", model);
        }

        private static void XFormBeforeSubmitPostedData(XFormPostedData xFormpostedData, Guid pageGuid)
        {
            DataStoreProvider dataStoreProvider = DataStoreProvider.CreateInstance();

            // Get the datastore for the email settings, or create one
            DynamicDataStore store = DynamicDataStoreFactory.Instance.GetStore(typeof(XFormsEmailContent))
                                     ?? DynamicDataStoreFactory.Instance.CreateStore(typeof(XFormsEmailContent));

            // Get the setting for this form
            IQueryable<XFormsEmailContent> allItems =
                store.Items<XFormsEmailContent>().Where(xf => xf.FormId == (Guid)xFormpostedData.XForm.Id);

            // Get the <see cref="XFormsEmailContent"/> for this form.
            XFormsEmailContent xFormData = allItems.SingleOrDefault();

            // Set the pageguid of the form to the pageguid, sometimes it's empty
            xFormpostedData.XForm.PageGuid = pageGuid;

            if (xFormpostedData.SelectedSubmit.ChannelOptions == ChannelOptions.Email)
            {
                // Always update the settings
                if (xFormData != null)
                {
                    try
                    {
                        store.Delete(xFormData);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
                }

                // Create the settings object.
                xFormData = new XFormsEmailContent(
                    (Guid)xFormpostedData.XForm.Id,
                    xFormpostedData.SelectedSubmit.Sender,
                    xFormpostedData.SelectedSubmit.Receiver,
                    xFormpostedData.SelectedSubmit.Subject);

                dataStoreProvider.ExecuteTransaction(
                    () =>
                        {
                            // Set the DataStoreProvider on store
                            store.DataStoreProvider = dataStoreProvider;
                            store.Save(xFormData);
                        });

                // Remove the email option to prevent sending the single email and save to the database for backup.
                ElementFragment elementFragment = new ElementFragment();
                elementFragment.Attributes.Add(new AttributeFragment() { Name = "subject", IsEmpty = true });
                elementFragment.Attributes.Add(new AttributeFragment() { Name = "from", IsEmpty = true });
                elementFragment.Attributes.Add(new AttributeFragment() { Name = "to", IsEmpty = true });
                elementFragment.Attributes.Add(
                    new AttributeFragment() { Name = "name", Value = xFormpostedData.SelectedSubmit.SubmitId });
                elementFragment.Attributes.Add(
                    new AttributeFragment() { Name = "action", Value = XFormActionHelper.SEND_TO_DATABASE });

                SubmitFragment submitFragment = new SubmitFragment(elementFragment);
                xFormpostedData.SelectedSubmit = submitFragment;
            }
            else
            {
                if (xFormData != null)
                {
                    // Delete the setting, as the option has been changed.
                    dataStoreProvider.ExecuteTransaction(
                        () =>
                            {
                                // Set the DataStoreProvider on store
                                store.DataStoreProvider = dataStoreProvider;
                                store.Delete(xFormData);
                            });
                }
            }
        }
    }

You can register the posted, success and failed actions in your view like this

@Html.PropertyFor(x => x.CurrentPage.Form, new { XFormParameters = new EPiServer.XForms.Util.XFormParameters { PostAction = "Posted", SuccessAction = "Success", FailedAction = "Failed" } }) 

So why was it so difficult? I have no idea.

This is what I expected to work, but the formdata, like the ChannelOptions, were empty for some reason.

XFormActionHelper.BeforeSubmitPostedData += XFormActionHelper_BeforeSubmitPostedData;

So I opted for a PostAction. But changing the ChannelOption proved to be quite a challenge. I have no idea why it was setup the way it was and why the difference between the WebForms and MVC implementation. Creating a kind of xml fragments to edit some properties, instead of just setting the property, mwah.

The scheduled job and such stays the same of course, see my last post.

Hope this helps.

Leave a comment