Surendra Sharma

Surendra Sharma

Search This Blog

Thursday, February 23, 2017

Map WFFM Form fields with Word Mail Merge



In one of our requirement, we have to save WFFM form fields data in word document’s mail merge fields. This word document should be generated from mail merge template document.
 
I created WFFM form and created a new Save action under “/sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Save Actions” 

My WFFM form looks as

Sample WFFM Form
Sample WFFM Form


I bind the newly created Save action to this WFFM form.

I am using DocumentFormat.OpenXml to generate word document for mail merge fields.

Here is my word document template for mail merge fields

Word Mail Merge
Word Mail Merge


Below is code for this WFFM Save Action.

using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.Data;
using Sitecore.WFFM.Abstractions.Actions;
using System.IO;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;

namespace SitecorePOCinMVC.Web
{
    public class MyCustomAction : ISaveAction
    {
        public ID ActionID { get; set; }
        public string UniqueKey { get; set; }
        public ActionType ActionType { get; private set; }
        public ActionState QueryState(ActionQueryContext queryContext)
        {
            return ActionState.Enabled;
        }

        public ActionCallContext Context { get; set; }

        public void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext = null, params object[] data)
        {
            string companyName = adaptedFields.GetValueByFieldID("{083F0126-4912-4ED7-A4B4-D55869C73D67}");
            string companyID = adaptedFields.GetValueByFieldID("{5B128D2F-E671-4A07-BE7F-4BDE9385D4A9}");
            string entryDesc = adaptedFields.GetValueByFieldID("{C87FD86C-71A4-432F-8B78-9990F16F0477}");

            Dictionary<string, string> wffmFields = new Dictionary<string, string>();
            wffmFields.Add("Company Name", companyName);
            wffmFields.Add("Company ID", companyID);
            wffmFields.Add("Entry Desc", entryDesc);

            try
            {
                OpenXMLUtility.WFFMFieldsToMailMerge(wffmFields);
            }
            catch (Exception ex)
            {
                System.IO.File.AppendAllText("d:\\sitecoretext.txt", "PDF Error : -" + ex.Message);
            }
        }
    }

    class OpenXMLUtility
    {
        public static void WFFMFieldsToMailMerge(Dictionary<string, string> wffmFields)
        {

            string sourceFile = Path.Combine(@"D:\Sample Template.dotx");
            string destinationFile = Path.Combine(@"D:\" + DateTime.Now.ToString("ddMMyyyyhhmmss") + ".docx");

            // Don't continue if the template file name is not found
            if (!File.Exists(sourceFile))
            {
                throw new Exception(message: "TemplateFileName (" + sourceFile + ") does not exist");
            }

            // If the file is a DOTX file convert it to docx
            if (sourceFile.ToUpper().EndsWith("DOTX"))
            {
                RETURN_VAL resultValue = ConvertTemplate(sourceFile, destinationFile);

                if (!resultValue.Value)
                {
                    var result = resultValue;
                }
            }
            else
            {
                // Otherwise make a copy of the Word Document to the targetFileName
                File.Copy(sourceFile, destinationFile);
            }

            using (WordprocessingDocument doc = WordprocessingDocument.Open(destinationFile, true))
            {

                doc.ChangeDocumentType(WordprocessingDocumentType.Document);

                string FieldDelimeter = " MERGEFIELD ";

                var document = doc.MainDocumentPart.Document;

                foreach (var field in document.Descendants<FieldCode>())
                {
                    var fieldNameStart = field.Text.LastIndexOf(FieldDelimeter, System.StringComparison.Ordinal);
                    var fieldname = field.Text.Substring(fieldNameStart + FieldDelimeter.Length).Trim();

                    string newFieldName = field.Text.Replace(" MERGEFIELD  ", "").Replace("  \\* MERGEFORMAT ", "").Trim();

                    var splitChars = newFieldName.Split(new string[] { "\\b" }, StringSplitOptions.RemoveEmptyEntries);
                    var columnName = splitChars[0].Trim().Trim(new char[] { '"' });

                    if (splitChars.Length > 1)
                    {
                        var columnDesc = splitChars[1].Trim().Trim(new char[] { '"' });
                    }

                    // Go through all of the Run elements and replace the Text Elements Text Property
                    foreach (Run run in document.Descendants<Run>())
                    {
                        foreach (Text txtFromRun in run.Descendants<Text>().Where(a => a.Text.Contains("«" + columnName + "»")))
                        {
                            txtFromRun.Text = wffmFields[columnName];
                            break;
                        }
                    }
                }

                // If the Document has settings remove them so the end user doesn't get prompted to use the data source
                DocumentSettingsPart settingsPart = doc.MainDocumentPart.GetPartsOfType<DocumentSettingsPart>().First();

                var oxeSettings = settingsPart.Settings.Where(a => a.LocalName == "mailMerge").FirstOrDefault();

                if (oxeSettings != null)
                {
                    settingsPart.Settings.RemoveChild(oxeSettings);

                    settingsPart.Settings.Save();
                }

                doc.MainDocumentPart.Document.Save();

            }
        }

        // Struct to hold the return value and possible exception
        public struct RETURN_VAL
        {
            public bool Value;
            public string Exception;
        }

        /// <summary>
        /// Converts the DOTX to DOCX
        /// </summary>
        /// <returns>True or False (with an exception) if successful in converting the document</returns>
        private static RETURN_VAL ConvertTemplate(string _templateFileName, string _targetFileName)
        {
            try
            {
                MemoryStream msFile = null;

                using (Stream sTemplate = File.Open(_templateFileName, FileMode.Open, FileAccess.Read))
                {
                    msFile = new MemoryStream((int)sTemplate.Length);
                    sTemplate.CopyTo(msFile);
                    msFile.Position = 0L;
                }

                using (WordprocessingDocument wpdFile = WordprocessingDocument.Open(msFile, true))
                {
                    wpdFile.ChangeDocumentType(DocumentFormat.OpenXml.WordprocessingDocumentType.Document);

                    MainDocumentPart docPart = wpdFile.MainDocumentPart;
                    docPart.AddExternalRelationship("http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate", new System.Uri(_templateFileName, UriKind.RelativeOrAbsolute));

                    docPart.Document.Save();
                }

                // Flush the MemoryStream to the file
                File.WriteAllBytes(_targetFileName, msFile.ToArray());

                msFile.Close();

                return new RETURN_VAL { Value = true };
            }
            catch (Exception ex)
            {
                return new RETURN_VAL { Value = false, Exception = "DocumentGeneration::convertTemplate() - " + ex.ToString() };
            }
        }
    }
}

After executing this code, my WFFM fields values replace the word mail merge fields as

Sitecore WFFM with Word Mail Merge
Sitecore WFFM with Word Mail Merge


I hope you like this Sitecore tip. Stay tuned for more Sitecore related articles.

Till that happy Sitecoring :)

Please leave your comments or share this tip if it’s useful for you.