Skip to content
Snippets Groups Projects
process.js 31.6 KiB
Newer Older
Johannes Hörmann's avatar
Johannes Hörmann committed
import("Binary_lib");
import("Communication_lib");
import("system.neon");
import("Employee_lib");
import("KeywordRegistry_basic");
import("Document_lib");
import("KeywordData_lib");
import("Sql_lib");
import("Address_lib");
import("system.process");
import("system.vars");
import("system.db");
import("system.util");
import("system.pack");
import("system.fileIO");
import("system.translate");
import("system.datetime");
import("system.text");
import("system.mail");
import("Keyword_lib");
import("Placeholder_lib");
import("Email_lib");
import("MimeType_lib");

/**
 * Object for working with document templates, holds the content and type of the template.
 * Provides functions to replace placeholders in the content.
 * 
 * @class
 */
var DocumentTemplate = (function ()
{

/**
 * constructor for DocumentTemplate
 * 
 * @param {String} pTemplateContent content, as base64 string (except for DocumentTemplate.types.PLAIN, then it's a normal string)
 * @param {String} pType type of the template, use the DocumentTemplate.types constants here
 * @param {String} [pFilename=undefined] file name of the template 
 * @param {Boolean} [pResolveSubtemplates=false] if true subtemplates are resolved (if the type fits)
 */
function DocumentTemplate(pTemplateContent, pType, pFilename, pResolveSubtemplates)
{
    this.content = pTemplateContent;
    this.type = pType;
    this.filename = pFilename;
    this._stringCache = null;
    this._subtemplatedContent = null;
Johannes Hörmann's avatar
Johannes Hörmann committed
    
    if (pResolveSubtemplates)
        this._resolveEmbeddedTemplate();
}

/**
 * @return {String} the text of the content
 */
DocumentTemplate.prototype.toString = function (pWithSubtemplates, pEmlOnlyBody)
Johannes Hörmann's avatar
Johannes Hörmann committed
{    
    if (this._stringCache == null)
    {
        var content = this._getTemplatedContent(pWithSubtemplates);
Johannes Hörmann's avatar
Johannes Hörmann committed
        if (this.type == DocumentTemplate.types.PLAIN)
            this._stringCache = content;
        else if (pEmlOnlyBody && this.type == DocumentTemplate.types.EML)
        {
            let email = Email.fromRFC(util.encodeBase64String(content))
            this._stringCache = email.body;
        }
Johannes Hörmann's avatar
Johannes Hörmann committed
        else
            this._stringCache = text.parseDocument(content);
Johannes Hörmann's avatar
Johannes Hörmann committed
    }
    return this._stringCache;
}

DocumentTemplate.prototype._resolveEmbeddedTemplate = function ()
{    
    // currently we support only txt and html as others would need special caution.
    if (this.content != null && (this.type == DocumentTemplate.types.TXT || this.type == DocumentTemplate.types.HTML))
Johannes Hörmann's avatar
Johannes Hörmann committed
    {
        var replacedContent = util.decodeBase64String(this.content);
Johannes Hörmann's avatar
Johannes Hörmann committed
        
        var templates = [];
        // then load the possible replacement names
        if (this.type == DocumentTemplate.types.TXT || this.type == DocumentTemplate.types.HTML)
Johannes Hörmann's avatar
Johannes Hörmann committed
        {
            templates = db.table(SqlCondition.begin()
                        .andPrepare("DOCUMENTTEMPLATE.KIND", $KeywordRegistry.documentTemplateType$textModular())
                        .andPrepare("DOCUMENTTEMPLATE.CLASSIFICATION", $KeywordRegistry.documentTemplateTypeCategory$textTemplate())
                        .buildSql("select DOCUMENTTEMPLATEID, REPLACEMENTNAME from DOCUMENTTEMPLATE"));

        }
        
        if (this.type == DocumentTemplate.types.HTML)
Johannes Hörmann's avatar
Johannes Hörmann committed
        {
            templates.concat(db.table(SqlCondition.begin()
Johannes Hörmann's avatar
Johannes Hörmann committed
                        .andPrepare("DOCUMENTTEMPLATE.KIND", $KeywordRegistry.documentTemplateType$textModular())
                        .andPrepare("DOCUMENTTEMPLATE.CLASSIFICATION", $KeywordRegistry.documentTemplateTypeCategory$htmlTemplate())
                        .buildSql("select DOCUMENTTEMPLATEID, REPLACEMENTNAME from DOCUMENTTEMPLATE")));
Johannes Hörmann's avatar
Johannes Hörmann committed
        }
        
        var alias = SqlUtils.getSystemAlias();

        // We use callbacks which are called by pString.replace
        placeholders = templates.map(function(pTemplate) {
            // add function for each placeholder so that the db.getBinaryContent is only called, if the placeholder is replaced.
            return [pTemplate[1], function(matched, index, original) {
                var templateDocument = db.getBinaryMetadata("DOCUMENTTEMPLATE", "DOCUMENT", pTemplate[0], false, alias, null);
                var binaryId = templateDocument[0][db.BINARY_ID];
                return util.decodeBase64String(db.getBinaryContent(binaryId, alias));
            }];
        });

        // Note: some embedded templates in embedded templates may be replaced, but this is NOT SUPPORTED, as it only works if the second template is not already replaced at that time.
        placeholders.forEach(function(pPlaceholder) {
Johannes Hörmann's avatar
Johannes Hörmann committed
            replacedContent = replacedContent.replace("{@" + pPlaceholder[0] + "@}", pPlaceholder[1], "g")
Johannes Hörmann's avatar
Johannes Hörmann committed
        }, this);
        
        this._subtemplatedContent = util.encodeBase64String(replacedContent);
DocumentTemplate.prototype._getTemplatedContent = function (pWithSubtemplates) {
    if (this._subtemplatedContent != null && pWithSubtemplates)
        return this._subtemplatedContent;
        return this.content;
Johannes Hörmann's avatar
Johannes Hörmann committed
/**
 * The types a DocumentTemplate can have. Depending on the type,
 * the correct method for replacing the placeholders can be chosen
 * 
 * @enum {String}
 */
DocumentTemplate.types = {
    TXT : "txt",
    HTML : "html",
    EML : "eml",
    ODT : "odt",
    DOCX : "docx",
    PLAIN : "plain", //for simple strings
    
    /**
     * chooses the type depending on the file extension
     */
    fromFileExtension : function (pFileExtension)
    {
        switch (pFileExtension)
        {
            case "html":
            case "htm":
                return this.HTML;
            case "eml":
                return this.EML;
            case "odt":
                return this.ODT;
            case "docx":
                return this.DOCX;
            case "txt":
                return this.TXT;
            case "msg":
            case "oft":
            default:
                return null;
        }
    },
    /**
     * chooses the type depending on the mime type
     */
    fromMimeType : function (pMimetype)
    {
        switch (pMimetype)
        {
            case MimeTypes.HTML():
                return this.HTML;
            case MimeTypes.EML():
                return this.EML;
            case MimeTypes.ODT():
                return this.ODT;
            case MimeTypes.DOCX():
                return this.DOCX;
            case MimeTypes.TXT():
                return this.TXT;
            case MimeTypes.MSG():
            default:
                return null;
        }
    },
    /**
     * chooses the type depending on the extension in the metadata. If the extension doesn't work, try mimetype
     * @param {String[]} pBinaryMetadata the binary metadata from system.db
     */
    fromBinaryMetadata: function(pBinaryMetadata)
    {
        let filename = pBinaryMetadata[db.BINARY_FILENAME].split(".");
        let type = DocumentTemplate.types.fromFileExtension(filename[filename.length - 1]);
        if (type == null)
            type = DocumentTemplate.types.fromMimeType(pBinaryMetadata[db.BINARY_MIMETYPE]);
        
        return type;
Johannes Hörmann's avatar
Johannes Hörmann committed
    }
};

/**
 * Loads the content of a document template and creates a new DocumentTemplate object with that.
 * 
 * @param {String} pAssignmentRowId id of the assignment (in most cases the document template id)
 * @param {String} [pAssignmentTable="DOCUMENTTEMPLATE"] the LOB assignment table
 * @param {Boolean} [pResolveSubtemplates=true] if true subtemplates are resolved (if the type fits)
 * @return {DocumentTemplate} template object
 */
DocumentTemplate.loadTemplate = function (pAssignmentRowId, pAssignmentTable, pResolveSubtemplates)
{
    var alias = SqlUtils.getSystemAlias();
    if (!pAssignmentTable)
        pAssignmentTable = "DOCUMENTTEMPLATE";
    var templateDocument = db.getBinaryMetadata(pAssignmentTable, "DOCUMENT", pAssignmentRowId, false, alias, null);
    if (!templateDocument[0])
        return new DocumentTemplate();
    var binaryId = templateDocument[0][db.BINARY_ID];
    var filename = templateDocument[0][db.BINARY_FILENAME];
    var type = DocumentTemplate.types.fromBinaryMetadata(templateDocument[0]);
Johannes Hörmann's avatar
Johannes Hörmann committed
    
    if (pResolveSubtemplates == undefined) pResolveSubtemplates = true;
    
    return new DocumentTemplate(db.getBinaryContent(binaryId, alias), type, filename, pResolveSubtemplates);
}

/**
 * makes a DocumentTemplate from a upload value
 * 
 * @param {FileUpload} pDocumentUpload FileUpload object
 * @return {DocumentTemplate} a document template
 */
DocumentTemplate.fromUpload = function (pDocumentUpload)
{
    var type;
    
    //if the mimetype couldn't be determined, check the file extension
    if (pDocumentUpload.mimeType == MimeTypes.BIN())
        type = DocumentTemplate.types.fromFileExtension(pDocumentUpload.fileExtension);
    else
        type = DocumentTemplate.types.fromMimeType(pDocumentUpload.mimeType);
    
    return new DocumentTemplate(pDocumentUpload.bindata, type, pDocumentUpload.filename, true);
}

/**
 * Function that helps to get the correct template when editing a bulk mail.
 * pUploadValue is preferred over pTemplateId, and if pEditedContent is provided,
 * it will overwrite the content of the template (but the type will remain the same as
 * defined by the upload or templateId, if both are empty, pDefaultType is used)
 * 
 * @param {String} pTemplateId
 * @param {FileUpload} pDocumentUpload
 * @param {String} [pEditedContent]
 * @param {String} [pDefaultType]
 * @return {DocumentTemplate} the document template
 */
DocumentTemplate.getSelectedTemplate = function (pTemplateId, pDocumentUpload, pEditedContent, pDefaultType)
{
    if (pDocumentUpload.isFilled())
        template = DocumentTemplate.fromUpload(pDocumentUpload);
    else if (pTemplateId)
        template = DocumentTemplate.loadTemplate(pTemplateId);
    else
        template = new DocumentTemplate(null, pDefaultType || DocumentTemplate.types.TXT, null, true);
    
    if (pEditedContent)
    {
        if (template.type == DocumentTemplate.types.EML || template.type == DocumentTemplate.types.HTML)
            pEditedContent = "<html>" + pEditedContent + "</html>";
        template.content = util.encodeBase64String(pEditedContent);
    }
    return template;
}

/** 
 * Returns the template content with replaced placeholders by choosing the right
 * replace function for the type.
 * 
 * @param {Object} pReplacements map, the structure is {placeholder : value}
 * @param {boolean} pEncoded if the replaced content should be base64 encoded
 *                            (doesn't affect odt/docx)
 * 
 * @return {String} the replaced content
 */
DocumentTemplate.prototype.getReplacedContent = function (pReplacements, pEncoded, pEmlOnlyBody)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    // if there exists a _subtemplatedContent we use it because then I assume that the replacements are already based on content + subtemplates
    if (!pEmlOnlyBody)
    {
        var content;
        if (this._subtemplatedContent == null)
            content = this.content
        else
            content = this._subtemplatedContent
    }
Johannes Hörmann's avatar
Johannes Hörmann committed
    switch (this.type)
    {
        case DocumentTemplate.types.HTML:
            for (let i in pReplacements)
                pReplacements[i] = text.text2html(pReplacements[i], false);
        case DocumentTemplate.types.TXT:
            let decodedContent = util.decodeBase64String(content);
            let encodedContent = TemplateHelper._replaceText(decodedContent, pReplacements, TemplateHelper._getSpecialRegexp(this));
Johannes Hörmann's avatar
Johannes Hörmann committed
            if (pEncoded)
                encodedContent = util.encodeBase64String(encodedContent);
            return encodedContent;
        case DocumentTemplate.types.EML:
            let emlContent
            if (pEmlOnlyBody)
                emlContent = this.toString(true, true)
            else
                emlContent = util.decodeBase64String(content);
            emlContent = TemplateHelper._replaceText(emlContent, pReplacements, TemplateHelper._getSpecialRegexp(this));
Johannes Hörmann's avatar
Johannes Hörmann committed
            if (pEncoded)
                emlContent = util.encodeBase64String(emlContent);
            return emlContent;
        case DocumentTemplate.types.ODT:
            return TemplateHelper._getReplacedODT(this, pReplacements);
        case DocumentTemplate.types.DOCX:
            return TemplateHelper._getReplacedDOCX(this, pReplacements);
        case DocumentTemplate.types.PLAIN:
            let plainText = TemplateHelper._replaceText(this.content, pReplacements, TemplateHelper._getSpecialRegexp(this));
Johannes Hörmann's avatar
Johannes Hörmann committed
            if (pEncoded)
                plainText = util.encodeBase64String(plainText);
            return plainText;
        default:
            return null;
    }
}

/**
 * replaces the placeholders with data from one contact and returns the result
 */
DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId, pEncoded, pEmlOnlyBody) 
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    var replacements = TemplateHelper._getReplacementsByContactIds(this, [pContactId]); 
    var content = this.getReplacedContent(replacements[pContactId], pEncoded, pEmlOnlyBody);
Johannes Hörmann's avatar
Johannes Hörmann committed
    
    return content;
}

/**
 * replaces the placeholders with data from the contacts and returns the result
 * 
 * @param {Array} pContactIds contact ids
 * @param {boolean} pEncoded if the replaced content should be base64 encoded
 * 
 * @return {Object} replaced content for every contactId
 */
DocumentTemplate.prototype.getReplacedContentByContactIds = function (pContactIds, pEncoded) 
{
    var replacements = TemplateHelper._getReplacementsByContactIds(this, pContactIds);
    var contents = {};
    for (let contactId in replacements)
    {
        contents[contactId] = this.getReplacedContent(replacements[contactId], pEncoded);
    }
    return contents;
}

/**
 * Replaces the placeholders with data from the contacts and returns a serial letter, works
 * only for ODT
 * 
 * @param {Array} pContactIds contact ids
 * @param {Object[][][]} pTableData Table data for the document, as a three-dimensional array 
 *          of objects (dimensions are: document, table in that document, rows of the table). For the format, see example.
 * 
 * @example 
 * var contactSql = SqlCondition.begin()
 *       .andPrepare("CONTACT.ORGANISATION_ID", orgId)
 *       .buildSql("select FIRSTNAME, LASTNAME from CONTACT join PERSON on CONTACT.PERSON_ID = PERSON.PERSONID");
 *
 *   var contacts = db.table(contactSql);
 *   var tblRows = [];
 *   fnamePlaceholder = PlaceholderUtils.formatPlaceholder("fname");
 *   lnamePlaceholder = PlaceholderUtils.formatPlaceholder("lname");
 *
 *   for (let i = 0; i < contacts.length; i++)
 *   {
 *       let names = {};
 *       names[fnamePlaceholder] = contacts[i][0];
 *       names[lnamePlaceholder] = contacts[i][1];
 *       tblRows.push(names);
 *   }
 *
 *   var tables = [tblRows];
 *   var template = DocumentTemplate.loadTEmplate(templateId);
 *   var letter = template.getSerialLetterByContactIds([contactId], [tables]);
 * 
 * @return {Object} the content of the replaced ODT
 */
DocumentTemplate.prototype.getSerialLetterByContactIds = function (pContactIds, pTableData)
{
    if (this.type == DocumentTemplate.types.ODT)
    {
        let replacements = TemplateHelper._getReplacementsByContactIds(this, pContactIds);
        let replaceArray = [];
        for (let i = 0, l = pContactIds.length; i < l; i++)
            replaceArray.push(replacements[pContactIds[i]]);
        return TemplateHelper._getReplacedODT(this, replaceArray, pTableData);
    }
    return null;
}

/**
 * Replaces the placeholders with data from the contacts and returns the resulting Emails.
 * 
 * @param {Array} pContactIds contact ids
 * 
 * @return {Object} Object containing the contact ids as keys and the corresponding Email
 *                   objects as values
 */
DocumentTemplate.prototype.getReplacedEmailsByContactIds = function (pContactIds) 
{
    var replacements = TemplateHelper._getReplacementsByContactIds(this, pContactIds);
    var emailObj = {};
    for (let contactId in replacements)
    {
        if (this.type == DocumentTemplate.types.EML)
        {
            emailObj[contactId] = new Email();
            emailObj[contactId].emlFile = this.getReplacedContent(replacements[contactId])
Johannes Hörmann's avatar
Johannes Hörmann committed
        }
        else
        {
            let body = this.getReplacedContent(replacements[contactId]);
            if (this.type == DocumentTemplate.types.TXT || this.type == DocumentTemplate.types.PLAIN)
                body = text.text2html(body, false);
            emailObj[contactId] = new Email();
            emailObj[contactId].body = body
Johannes Hörmann's avatar
Johannes Hörmann committed
        }
    }
    return emailObj;
}

/**
 * Provides functions for the DocumentTemplate object that aren't accessible from outside
 */
function TemplateHelper () {}
TemplateHelper._replaceText = function (pText, pReplacements, pSpecialCharFilterRegexpPart)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    if (pSpecialCharFilterRegexpPart == undefined) pSpecialCharFilterRegexpPart = "";
    
    pText = pText.replace(new RegExp(PlaceholderUtils.getRegexpMatchAll(pSpecialCharFilterRegexpPart), "gi"), function(pFound) {
        let foundFiltered = pFound.replace(new RegExp(pSpecialCharFilterRegexpPart, "gi"),"");
        return pReplacements[foundFiltered] ? pReplacements[foundFiltered] : pFound;
    }, "gi")

    return pText;        
 * @param {DocumentTemplate} pTemplate
 * @return {Object[]} all placeholders needed in this template
Johannes Hörmann's avatar
Johannes Hörmann committed
 * @private
 */
TemplateHelper._getRequiredPlaceholders = function (pTemplate)
    var content = "";
    // for eml search the whole file not just the body text as placeholders could be anywhere (e.g. subject)
    content = pTemplate.toString(true, false);
    // get special regexp (e.g. to filter '=' in emls)
    var filterRegexpPart = TemplateHelper._getSpecialRegexp(pTemplate);    
    var placeholders = PlaceholderUtils.getPlaceholders();

    var placeholderCleanRegexp = new RegExp(filterRegexpPart, "gi");
    
    // get all placeholders which matches the placeholder pattern
    var foundPlaceholders = content.match(new RegExp(PlaceholderUtils.getRegexpMatchAll(TemplateHelper._getSpecialRegexp(pTemplate)), "gi"));
    
    if (foundPlaceholders != null)
        // clean placeholder from the spechial strings (e.g. to filter '=' in emls)
        foundPlaceholders = foundPlaceholders.map(function(pFound) {
            return pFound.replace(placeholderCleanRegexp,"");
        });

        // filter the possible placeholders by all placeholders found
        placeholders = placeholders.filter(function(pPlaceholder)
        {
            return foundPlaceholders.indexOf(pPlaceholder.placeholderName) != -1;
        });
        logging.log(JSON.stringify([placeholders], null, "\t"))
        return placeholders;
    }
    
    return [];
Johannes Hörmann's avatar
Johannes Hörmann committed
}

/**
 * Builds an object with the placeholder data for multiple contacts
 * 
 * @param {DocumentTemplate} pTemplate document template
 * @param {Array} pContactIds contact ids
 * 
 * @return {Object} Object containing the data. The structure is like {contactId : {placeholderName : replacementValue, ...}, ...}
 * 
 * @private
 */
TemplateHelper._getReplacementsByContactIds = function (pTemplate, pContactIds)
{ 
    var config = TemplateHelper._getRequiredPlaceholders(pTemplate);
    var contactIdPlaceholder = new Placeholder("contactId", Placeholder.types.SQLPART, "CONTACT.CONTACTID");
    config = [contactIdPlaceholder].concat(config);

    var addressData = getAddressesData(pContactIds, config, EmployeeUtils.getCurrentContactId()); //TODO: add sender selection
    var replacements = {};
    var placeholderNames = addressData[0];
    var contactIdIndex = placeholderNames.indexOf(contactIdPlaceholder.toString());
    for (let i = 1; i < addressData.length; i++)
    {
        let contactId = addressData[i][contactIdIndex];
        for (let ii = 0, ll = placeholderNames.length; ii < ll; ii++)
        {
            if (!(contactId in replacements))
                replacements[contactId] = {};
            replacements[contactId][placeholderNames[ii]] = addressData[i][ii];
        }
    }
    return replacements;
}

/*
 * replaces a given Odt-File on the server and returns the replaced base64-file
 *
 * @param {DocumentTemplate} pTemplate document template
 * @param {Object} pReplacements map of placeholders and replacements
 * @param {Array} pTableData
 *
 * @return {String} base64-encoded replaced file
 * 
 * @private
 */
TemplateHelper._getReplacedODT = function (pTemplate, pReplacements, pTableData)
{
    //save the file on the server so it can be unzipped via pack.getFromZip
    var serverFilePath = vars.get("$sys.servertemp") + "/clientid_" + vars.get("$sys.clientid")
        + "/" + util.getNewUUID() + "/" + pTemplate.filename.replace(/\\/g, "/");
    
    fileIO.storeData(serverFilePath, pTemplate.content, util.DATA_BINARY, false);
    if (!_replaceODTFile(pReplacements, serverFilePath, pTableData))
        return null;

    var replacedFileData = fileIO.getData(serverFilePath, util.DATA_BINARY);
    fileIO.remove(serverFilePath);

    return replacedFileData;
    
    /**
    * replaces placeholders in a odt file
    *
    * @param {Object} pReplacements replacement object
    * @param {String} pODTFileName filename of the odt
    * @param {Array} pTableData
    * 
    * @return {Boolean}
    */
    function _replaceODTFile (pReplacements, pODTFileName, pTableData)
    {
        var senderRelId = EmployeeUtils.getCurrentContactId();
        if (senderRelId == null)
            return false;
        if (pReplacements.length === undefined)
            pReplacements = [pReplacements];
        if (!pTableData)
            pTableData = [];
        
        if (pReplacements.length !== 0)
        {
            //replace placeholders in content.xml
            var contentXml = util.decodeBase64String(pack.getFromZip(pODTFileName, "content.xml"));
            var bodybegin = contentXml.indexOf("<office:body>");
            var bodyend =  contentXml.indexOf("</office:body>") + 14;
            
            var bodyTemplate = contentXml.substring(bodybegin, bodyend);
            var fullBody = "";  //body that contains all pages (required when the replacing is done for several contacts)
            var beforeBody = contentXml.substring(0, bodybegin);
            var afterBody = contentXml.substr(bodyend);
            
            for (let i = 0, l = pReplacements.length; i < l; i++)
            {
                let replacements = pReplacements[i];
                let currentBody = bodyTemplate;
            
                /* This only works if the text of the placeholders in the odt were not edited since they were written.
                 * If you edit the odt and change a placeholder (for example: you change '{@addres@}' to '{@address@}'),
                 * the text is saved in different XML tags and won't be replaced correctly.
                 */
                for (let placeholder in replacements)
                {
                    currentBody = currentBody.replace(placeholder,
                        replacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&amp;"), "ig");
                }
                
                
                let tables = pTableData[i] || [];
                let fromIndex = 0;
                for (let tblIndex = 0; tblIndex < tables.length; tblIndex++) //iterate over all tables in the document
                {
                    let tableData = tables[tblIndex];
                    if (tableData && tableData.length > 0)
                    {
                        fromIndex = currentBody.indexOf("</table:table>", fromIndex) + 14;
                        if (fromIndex === -1) //stop if there is no table
                            break;
                        
                        let rowBegin = currentBody.lastIndexOf("<table:table-row", fromIndex);
                        let rowEnd =  currentBody.indexOf("</table:table-row>", rowBegin) + 18;
                        
                        let afterTable = currentBody.substr(rowEnd);
                        let tableRow = currentBody.substring(rowBegin, rowEnd);
                        currentBody = currentBody.substring(0, rowBegin);
                        
                        for (let rowIndex = 0; rowIndex < tableData.length; rowIndex++)
                        {
                            let tableRowData = tableData[rowIndex];
                            currentBody += TemplateHelper._replaceText(tableRow, tableRowData);
                        }
                        currentBody += afterTable;
                    }
                }
                fullBody += currentBody;
            }   
            contentXml = beforeBody + fullBody + afterBody;
            pack.addToZip(pODTFileName, "content.xml", util.encodeBase64String(contentXml));
            
            //replace placeholders in styles.xml
            var styles = util.decodeBase64String(pack.getFromZip(pODTFileName, "styles.xml"));
            for (let placeholder in pReplacements[0])
            {
                styles = styles.replace(placeholder,
                    pReplacements[0][placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&amp;"), "ig");
            }
            pack.addToZip(pODTFileName, "styles.xml", util.encodeBase64String(styles));
            return true;
        }
        return false;
    }
}

/*
 * This function is used to replace placeholders via DocXTemplater
 * 
 * @param {DocumentTemplate} pTemplate document template
 * @param {Object} pReplacements - Must contain an object, which holds the placeholders
 * 
 * @return {String} returns the modified document in a BASE64 coded string
 * 
 * @private
 */
TemplateHelper._getReplacedDOCX = function (pTemplate, pReplacements)
{
    var replacements = {};
    for (let placeholder in pReplacements)  //removes the prefix and postfix, the process needs it like this
        replacements[placeholder.slice(2, -2)] = pReplacements[placeholder];

    //this is executed as a process because of better performance
    var documentData = process.execute("getDocxDocument_serverProcess", {
        templateb64: pTemplate.content,
        placeholderConfig: JSON.stringify(replacements) //process.execute is only able to handle strings
    });

    return documentData;
}

TemplateHelper._getSpecialRegexp = function (pTemplate)
{
    switch (pTemplate.type)
    {
        case DocumentTemplate.types.EML:
            return "\\s*(=\\r?\\n)?";
        default:
            return "";
    }
}

Johannes Hörmann's avatar
Johannes Hörmann committed
    return DocumentTemplate;

})();

/**
 * functions for working with letters (mails)
 */
function LetterUtils () {}

/**
 * opens a new letter
 * 
 * @param {String} pContactId id of the contact to fetch the data from
 */
LetterUtils.openNewLetter = function (pContactId)
{
    var params = {
        "ContactId_param" : pContactId
    };
    neon.openContext("Letter", "LetterEdit_view", null, neon.OPERATINGSTATE_NEW, params);
}

/**
 * utility functions for the DocumentTemplate_entity
 */
function DocumentTemplateUtils () {}

/**
 * if pText is provided, it is used as template, otherwise pFileUpload
 * 
 * @param {FileUpload} pFileUpload upload value
 * @param {String} pKind kind of template
Johannes Hörmann's avatar
Johannes Hörmann committed
 * @param {String} pText text input
 * @param {String} pClassification the classification type. Used if pText is not empty. Defines if it is saved as txt or html.
 * @param {String} pTemplateName name of the template
 * @return {FileUpload} a FileUpload object with the data
 */
DocumentTemplateUtils.chooseSuppliedTemplate = function (pFileUpload, pKind, pText, pClassification, pTemplateName)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    if (pFileUpload.isFilled() && pText != "")
    {
        // use fileUpload but use the custom text as bindata
        pFileUpload.bindata = util.encodeBase64String(pText);
    }
    else if (!pFileUpload.isFilled() && pText != "")
Johannes Hörmann's avatar
Johannes Hörmann committed
    {
        pFileUpload.filename = pTemplateName;
        
        // if it is a htmlTemplate save it with the html extension
        if (pClassification == $KeywordRegistry.documentTemplateTypeCategory$htmlTemplate() || pKind == $KeywordRegistry.documentTemplateType$mail())
Johannes Hörmann's avatar
Johannes Hörmann committed
            pFileUpload.fileExtension = "html";
        else
            pFileUpload.fileExtension = "txt";
        
        pFileUpload.bindata  = util.encodeBase64String(pText);
    }
    return pFileUpload;
}

/**
 * inserts a template from a document template into ASYS_BINARIES
 */
DocumentTemplateUtils.insertTemplateData = function (pTemplateId, pFileUpload, pKind, pText, pClassification, pTemplateName)
{
    pFileUpload = DocumentTemplateUtils.chooseSuppliedTemplate(pFileUpload, pKind, pText, pClassification, pTemplateName)
Johannes Hörmann's avatar
Johannes Hörmann committed
    if (pFileUpload.isFilled())
    {
        db.insertBinary("DOCUMENTTEMPLATE", "DOCUMENT", pTemplateId, 
            "", pFileUpload.bindata, pFileUpload.filename, "", "TEMPLATE", SqlUtils.getSystemAlias());
Johannes Hörmann's avatar
Johannes Hörmann committed
    }
}

/**
 * updates a template from a document template in ASYS_BINARIES
 */
DocumentTemplateUtils.updateTemplateData = function (pTemplateId, pFileUpload, pKind, pText, pClassification, pTemplateName)
{
    pFileUpload = DocumentTemplateUtils.chooseSuppliedTemplate(pFileUpload, pKind, pText, pClassification, pTemplateName)
Johannes Hörmann's avatar
Johannes Hörmann committed
    if (pFileUpload.isFilled())
    {
        var assignmentTable = "DOCUMENTTEMPLATE";
        var assignmentName= "DOCUMENT";
        var keyword = "TEMPLATE";
        var binMeta = db.getBinaryMetadata(assignmentTable, assignmentName, pTemplateId, false, SqlUtils.getSystemAlias(), keyword);
Johannes Hörmann's avatar
Johannes Hörmann committed
        if (binMeta.length == 0)
            SingleBinaryUtils.insertMainDocument(assignmentTable, assignmentName, pTemplateId, pFileUpload.bindata, pFileUpload.filename, null, SqlUtils.getSystemAlias());
        else
            db.updateBinary(binMeta[0][db.BINARY_ID], "", pFileUpload.bindata, pFileUpload.filename, "", keyword, SqlUtils.getSystemAlias());
    }
}

/**
 * loads content from a fileUpload or if it's empty from a template in ASYS_BINARIES
 * @param {String} pTemplateId the id of the template
 * @param {FileUpload} pFileUpload upload object
 * 
 * @return {String} content or ""
 */
DocumentTemplateUtils.getTemplateContent = function (pTemplateId, pFileUpload)
{
    var type;
    var bindata;
    
    if (pFileUpload.isFilled())
    {
        type = DocumentTemplate.types.fromFileExtension(pFileUpload.fileExtension);
        bindata = pFileUpload.bindata
    }
    else
    {
        var assignmentTable = "DOCUMENTTEMPLATE";
        var assignmentName= "DOCUMENT";
        var keyword = "TEMPLATE";
        
        var binMeta = db.getBinaryMetadata(assignmentTable, assignmentName, pTemplateId, false, SqlUtils.getSystemAlias(), keyword);
        
        if (binMeta.length != 0)
        {           
            let binMeta = db.getBinaryMetadata(assignmentTable, assignmentName, pTemplateId, false, SqlUtils.getSystemAlias(), keyword);
            bindata = db.getBinaryContent(binMeta[0][db.BINARY_ID], SqlUtils.getSystemAlias());
            type = DocumentTemplate.types.fromBinaryMetadata(binMeta[0]);
        }
    }
    
    if (type == DocumentTemplate.types.HTML || type == DocumentTemplate.types.TXT)
    {
        return util.decodeBase64String(bindata);
    }
    
    return "";
}

/**
 * loads the type from a fileUpload or if it's empty from a template in ASYS_BINARIES
 * @param {String} pTemplateId the id of the template
 * @param {FileUpload} pFileUpload upload object
 * 
 * @return {String} type via DocumentTemplate.types or null
 */
DocumentTemplateUtils.getContentType = function (pTemplateId, pFileUpload)
{
    var type;
    
    if (pFileUpload.isFilled())
    {
        type = DocumentTemplate.types.fromFileExtension(pFileUpload.fileExtension);
    }
    else
    {
        var assignmentTable = "DOCUMENTTEMPLATE";
        var assignmentName= "DOCUMENT";
        var keyword = "TEMPLATE";
        
        var binMeta = db.getBinaryMetadata(assignmentTable, assignmentName, pTemplateId, false, SqlUtils.getSystemAlias(), keyword);
        
        if (binMeta.length != 0)
        {           
            let binMeta = db.getBinaryMetadata(assignmentTable, assignmentName, pTemplateId, false, SqlUtils.getSystemAlias(), keyword);
            type = DocumentTemplate.types.fromBinaryMetadata(binMeta[0]);
        }
Johannes Hörmann's avatar
Johannes Hörmann committed
    }