Skip to content
Snippets Groups Projects
process.js 30.3 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;
    
    if (pResolveSubtemplates)
        this._resolveEmbeddedTemplate();
}

/**
 * @return {String} the text of the content
 */
DocumentTemplate.prototype.toString = function ()
{    
    if (this._stringCache == null)
    {
        if (this.type == DocumentTemplate.types.PLAIN)
            this._stringCache = this.content;
        else
            this._stringCache = text.parseDocument(this.content);
    }
    return this._stringCache;
}

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

        }
        else if (this.type == DocumentTemplate.types.HTML)
        {
            templates = db.table(SqlCondition.begin()
                        .andPrepare("DOCUMENTTEMPLATE.KIND", $KeywordRegistry.documentTemplateType$textModular())
                        .andPrepare("DOCUMENTTEMPLATE.CLASSIFICATION", $KeywordRegistry.documentTemplateTypeCategory$htmlTemplate())
                        .buildSql("select DOCUMENTTEMPLATEID, REPLACEMENTNAME from DOCUMENTTEMPLATE"));
        }
        
        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.content = util.encodeBase64String(replacedContent);
    }
}

/**
 * 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
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
    
    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)
{
    // if there exists a _subtemplatedContent we use it because then I assume that the replacements are already based on content + subtemplates
    var content;
    if (this._subtemplatedContent == null)
        content = this.content
    else
        content = this._subtemplatedContent
    
    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);
            if (pEncoded)
                encodedContent = util.encodeBase64String(encodedContent);
            return encodedContent;
        case DocumentTemplate.types.EML:
            let emlContent = TemplateHelper._getReplacedEML(this, pReplacements);
            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);
            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) 
{
    var replacements = TemplateHelper._getReplacementsByContactIds(this, [pContactId]); 
    var content = this.getReplacedContent(replacements[pContactId], pEncoded);
    
    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)
        {
            //use the special function for EML to also fill subject and sender
            emailObj[contactId] = TemplateHelper._getReplacedEML(this, replacements[contactId], true);
        }
        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(null, null, null, body);
        }
    }
    return emailObj;
}

/**
 * Provides functions for the DocumentTemplate object that aren't accessible from outside
 */
function TemplateHelper () {}
/**
 * Replace function that works with strings instead of regular expressions
 * so that control characters (for example '{', '}') don't have to be escaped.
 */
TemplateHelper._replaceText = function (pText, pReplacements)
{
    for (let placeholder in pReplacements)
        pText = pText.replace(placeholder, pReplacements[placeholder], "ig");
    return pText;
}

/**
 * returns the 'simpleName' of all placeholders that are used in the template
 * 
 * @private
 */
TemplateHelper._getRequiredPlaceholders = function (pTemplate)
{
    var allPlaceholders = PlaceholderUtils.getPlaceholders();
    var plainText = pTemplate.toString();
    var usedPlaceholders = [];
    for (let i = 0, l = allPlaceholders.length; i < l; i++)
    {
        if (plainText.indexOf(allPlaceholders[i]) !== -1)
            usedPlaceholders.push(allPlaceholders[i]);
    }
    return usedPlaceholders; 
}

/**
 * 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 placeholders for EML
 * 
 * @param {DocumentTemplate} pTemplate document template
 * @param {Object} pReplacements mapping with replacements for every placeholder
 * @param {boolean} [pGetEmail] if true, return an Email object (use this if the sender and subject are required)
 * 
 * @return {String|Email} the replaced content
 * 
 * @private
 */
TemplateHelper._getReplacedEML = function (pTemplate, pReplacements, pGetEmail)
{
    var email = Email.fromRFC(pTemplate.content);
    email.body = TemplateHelper._replaceText(email.body, pReplacements);
    if (!pGetEmail)
        return email.body;
    
    email.sender = TemplateHelper._replaceText(email.sender, pReplacements);
    email.subject = TemplateHelper._replaceText(email.subject, pReplacements);
    
    return email;
}

/*
 * 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;
}

    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
    }