Newer
Older
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.question");
import("system.text");
import("system.mail");
import("Keyword_lib");
import("Placeholder_lib");
import("Email_lib");
import("MimeType_lib");
S.Listl
committed
import("Util_lib");
/**
* Object for working with document templates, holds the content and type of the template.
* Provides functions to replace placeholders in the content.
*
* @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)
* @param {String} [pTemplateId=undefined] Provide it, if you have it, because this enables the template to load attachments associated by this ID
* @param {String} [pMimeType=undefined] mimetype of the content. Only an additional information. Not mandatory.
function DocumentTemplate (pTemplateContent, pType, pFilename, pResolveSubtemplates, pTemplateId, pMimeType)
{
this.content = pTemplateContent;
this.type = pType;
this.options = DocumentTemplate.types.getDefaultTypeOptions(pType);
this.templateId = pTemplateId;
this._attachmentCache = null;
this._subtemplateResolvedContent = null;
//cache used for .toString
this._stringCache = {
onlyContent : null,
withSubtemplatesResolved : null
};
this._resolveSubtemplates();
}
/**
* @return {String} the text of the content
*/
DocumentTemplate.prototype.toString = function (pWithSubtemplates)
var stringCachePosition = pWithSubtemplates ? "withSubtemplatesResolved" : "onlyContent";
if (this._stringCache[stringCachePosition] == null)
var content = pWithSubtemplates && this._subtemplateResolvedContent || this.content;
this._stringCache[stringCachePosition] = content;
this._stringCache[stringCachePosition] = text.parseDocument(content);
return this._stringCache[stringCachePosition];
/**
* Defines options for the DocumentTemplate, the given options will be appended to the existing options and options that
* already exist will be overridden. The options that can be used may vary depending on the type of the DocumentTemplate,
* that's why they are wrapped inside an object.
*
* @param {Object} pOptions The options to set for the object. Some options only affect special types, for example:
* <ul>
* <li>base64 (boolean): If the replaced content should be base 64 encoded (does not work for docx and odt, these are always encoded)</li>
* <li>onlyBody (boolean): If set to true, only use the body of an eml (option only for eml)</li>
* </ul>
* @return {DocumentTemplate} current object
*/
DocumentTemplate.prototype.setOptions = function (pOptions)
{
Object.assign(this.options, pOptions);
return this;
}
/**
* resolves sub-template placeholders
*/
DocumentTemplate.prototype._resolveSubtemplates = 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))
var replacedContent = util.decodeBase64String(this.content);
var templates = [];
// then load the possible replacement names
if (this.type == DocumentTemplate.types.HTML)
templates = templates.concat(newSelect("DOCUMENTTEMPLATEID, REPLACEMENTNAME").from("DOCUMENTTEMPLATE")
.where("DOCUMENTTEMPLATE.KIND", $KeywordRegistry.documentTemplateType$textModular())
.and("DOCUMENTTEMPLATE.CLASSIFICATION", $KeywordRegistry.documentTemplateTypeCategory$htmlTemplate())
.table());
var alias = SqlUtils.getBinariesAlias();
// 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.
S.Listl
committed
placeholders.forEach(function(pPlaceholder)
{
replacedContent = StringUtils.replaceAll(replacedContent, PlaceholderUtils.formatPlaceholder(pPlaceholder[0]), pPlaceholder[1], "g");
this._subtemplatedContent = util.encodeBase64String(replacedContent);
/**
* @return {DocumentTemmplate[]} if the templateId exists, it returns all attachments associated by the id as DocumentTemplate array else it just returns an empty array.
*/
DocumentTemplate.prototype.getAttachments = function ()
{
if (!this.templateId && this._attachmentCache == null)
return [];
}
if (this._attachmentCache == null)
{
var attachmentIds = newSelect("DOCUMENTTEMPLATE_ID_CHILD")
.from("DOCUMENTTEMPLATELINK")
.where("DOCUMENTTEMPLATELINK.DOCUMENTTEMPLATE_ID_PARENT", this.templateId)
.arrayColumn();
this._attachmentCache = attachmentIds.map(function(pAttachmentId)
{
return DocumentTemplateUtils.getTemplate(pAttachmentId, false);
});
return this._attachmentCache;
}
/**
* @param {DocumentTemplate[]} pAttachments array of templates which should be used as attachment
*/
DocumentTemplate.prototype.setAttachments = function (pAttachments)
{
this._attachmentCache = pAttachments;
/**
* 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;
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
},
/**
* Returns the default options for the given type.
*
* @param {String} pType the type
* @return {Object} object containing the default options
*/
getDefaultTypeOptions : function (pType)
{
switch (pType)
{
case this.EML:
return {
base64 : false,
onlyBody : false,
placeholderRegExp : /\{\s*(=\r?\n)?@(.(?!{@)|(\r?\n))+?@\s*(=\r?\n)?\}/gi,
parsePlaceholderFn : function (pPlaceholder)
{
return pPlaceholder.replace(/\s*(=\r?\n)?/, "");
}
};
case this.TXT:
case this.HTML:
return {
base64 : false
};
case this.DOCX:
case this.DOCM:
return {
startDelimiter : "{@",
endDelimiter : "@}"
};
case this.ODT:
default:
return {};
}
}
};
/**
* 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.getBinariesAlias();
if (!pAssignmentTable)
pAssignmentTable = "DOCUMENTTEMPLATE";
// if the templateId is accessable, use it, to enable the templateto load attachments
var templateId;
if (pAssignmentTable == "DOCUMENTTEMPLATE")
templateId = pAssignmentRowId;
if (templateId)
{

Johannes Hörmann
committed
var template = DocumentTemplateUtils.getTemplate(templateId, pResolveSubtemplates);
if (template == null)
{
if(vars.getString("$sys.isserver") == "true")
throw new Error("loadTemplate: File from this template could not be found anymore. "
+ "Please go to the template and upload a new file. TemplateID: '" + templateId + "'")
else
question.showMessage(translate.text("File from this template could not be found anymore. "
+ "Please go to the template and upload a new file."), question.ERROR, translate.text("Error"));

Johannes Hörmann
committed
return null;
}

Benjamin Ulrich
committed
var templateDocument;
if(pAssignmentTable == "SERIALLETTER")
{
templateDocument = db.getBinaryMetadata(pAssignmentTable, "SERIALLETTERFILE", pAssignmentRowId, false, alias, null);
}
else
{
templateDocument = db.getBinaryMetadata(pAssignmentTable, "DOCUMENT", pAssignmentRowId, false, alias, null);
}
return new DocumentTemplate(undefined, undefined, undefined, undefined, templateId);
var binaryId = templateDocument[0][db.BINARY_ID];
var filename = templateDocument[0][db.BINARY_FILENAME];
var mimeType = templateDocument[0][db.BINARY_MIMETYPE];
var type = DocumentTemplate.types.fromBinaryMetadata(templateDocument[0]);
if (pResolveSubtemplates == undefined)
pResolveSubtemplates = true;
return new DocumentTemplate(db.getBinaryContent(binaryId, alias), type, filename, pResolveSubtemplates, templateId, mimeType);
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
}
/**
* 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)

Johannes Hörmann
committed
{
template = DocumentTemplate.loadTemplate(pTemplateId);

Johannes Hörmann
committed
if (!template)
return null;
}
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}
*
* @return {String} the replaced content
*/
DocumentTemplate.prototype.getReplacedContent = function (pReplacements)
{
// if there exists a _subtemplatedContent we use it because then I assume that the replacements are already based on content + subtemplates
var content = this._subtemplateResolvedContent || this.content;
if (this.options.onlyBody)
{
// get only body and treat it as html (next case)
var email = Email.fromRFC(content);
content = util.encodeBase64String(email.body);
}
else
{
emlContent = util.decodeBase64String(content);
emlContent = this._replaceText(emlContent, pReplacements);
if (this.options.base64)
emlContent = util.encodeBase64String(emlContent);
return emlContent;
}
// replaces ä, ö, ü, ... with html escape signs
for (let i in pReplacements)
pReplacements[i] = text.text2html(pReplacements[i], false);
case DocumentTemplate.types.TXT:
let decodedContent = util.decodeBase64String(content);
let encodedContent = this._replaceText(decodedContent, pReplacements);
if (this.options.base64)
encodedContent = util.encodeBase64String(encodedContent);
return encodedContent;
case DocumentTemplate.types.ODT:
return this._getReplacedODT(pReplacements);
return this._getReplacedDOCX(pReplacements);
let plainText = this._replaceText(this.content, pReplacements);
if (this.options.base64)
plainText = util.encodeBase64String(plainText);
return plainText;
default:
return null;
}
}
/**
* replaces the placeholders with data from one contact and returns the result
*
* @param {String} pContactId contact id
* @param {Placeholder[]} pAdditionalPlaceholders Additional placeholders that should be used. You can use placeholders with the
* types FIXEDVALUE and CALLBACKFUNCTION if you want to calculate the replacement values yourself.
*
* @return {String} replaced content
DocumentTemplate.prototype.getReplacedContentByContactId = function (pContactId, pAdditionalPlaceholders)
var replacements = this.getReplacementsByContactIds([pContactId], pAdditionalPlaceholders)[pContactId];
var content = this.getReplacedContent(replacements);
return content;
}
/**
* replaces the placeholders with data from the contacts and returns the result
*
* @param {Array} pContactIds contact ids
* @param {Placeholder[]} pAdditionalPlaceholders Additional placeholders that should be used. You can use placeholders with the
* types FIXEDVALUE and CALLBACKFUNCTION if you want to calculate the replacement values yourself.
*
* @return {Object} replaced content for every contactId
*/
DocumentTemplate.prototype.getReplacedContentByContactIds = function (pContactIds, pAdditionalPlaceholders)
var replacements = this.getReplacementsByContactIds(pContactIds, pAdditionalPlaceholders);
var contents = {};
for (let contactId in replacements)
{
contents[contactId] = this.getReplacedContent(replacements[contactId]);
}
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.
* @param {Placeholder[]} pAdditionalPlaceholders Additional placeholders that should be used. You can use placeholders with the
* types FIXEDVALUE and CALLBACKFUNCTION if you want to calculate the replacement values yourself.
* var contacts = newSelect("FIRSTNAME, LASTNAME")
* .from("CONTACT")
* .join("PERSON", "CONTACT.PERSON_ID = PERSON.PERSONID")
* .where("CONTACT.ORGANISATION_ID", orgId)
* .table();
*
* 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, pAdditionalPlaceholders)
{
if (this.type == DocumentTemplate.types.ODT)
{
let replacements = this.getReplacementsByContactIds(pContactIds, pAdditionalPlaceholders);
let replaceArray = pContactIds.map(function (contactId)
{
return replacements[contactId];
});
return this._getReplacedODT(replaceArray, pTableData);
question.showMessage(DocumentTemplate.getSerialLetterODTOnlyMessage(), question.INFORMATION, translate.text("Action not supported"))
DocumentTemplate.getSerialLetterODTOnlyMessage = function()
{
return translate.text("Only .odt files are supported for bulkletters.");
}
/**
* Replaces the placeholders with data from the contacts and returns the resulting Emails.
*
* @param {Array} pContactIds contact ids
* @param {Placeholder[]} pAdditionalPlaceholders Additional placeholders that should be used. You can use placeholders with the
* types FIXEDVALUE and CALLBACKFUNCTION if you want to calculate the replacement values yourself.
*
* @return {Object} Object containing the contact ids as keys and the corresponding Email
* objects as values
*/
DocumentTemplate.prototype.getReplacedEmailsByContactIds = function (pContactIds, pAdditionalPlaceholders)
var emailObjects = {};
var isEML = this.type == DocumentTemplate.types.EML;
this.setOptions({base64 : isEML});
var emailContents = this.getReplacedContentByContactIds(pContactIds, pAdditionalPlaceholders);
for (let contactId in emailContents)
emailObjects[contactId] = Email.fromRFC(emailContents[contactId]);
if (this.type == DocumentTemplate.types.TXT || this.type == DocumentTemplate.types.PLAIN)
emailContents[contactId] = text.text2html(emailContents[contactId], false);
emailObjects[contactId] = new Email(emailContents[contactId]);
// adding the templates to each mail should be no memory-problem as it is only a reference to pTemplate._attachmentCache, so no problem here
emailObjects[contactId].attachmentTemplates = this.getAttachments();
/**
* replaces placeholders in the given string
*/
DocumentTemplate.prototype._replaceText = function (pText, pReplacements)
var placeholderRegExp = this.options.placeholderRegExp || PlaceholderUtils.getRegexpMatchAll();
var that = this;
pText = pText.replace(placeholderRegExp, function (pFound)
{
let foundFiltered = typeof that.options.parsePlaceholderFn === "function"
? that.options.parsePlaceholderFn(pFound)
: pFound;
return pReplacements[foundFiltered] ? pReplacements[foundFiltered] : pFound;
return pText;
* @param {String[]} pForcedPlaceholders these placeholders are always loaded
* @param {Placeholder[]} pAdditionalPlaceholders Additional placeholders that should be used. You can use placeholders with the
* types FIXEDVALUE and CALLBACKFUNCTION if you want to calculate the replacement values yourself.
* @return {Object[]} all placeholders needed in this template
DocumentTemplate.prototype._getRequiredPlaceholders = function (pForcedPlaceholders, pAdditionalPlaceholders)
var content = this.toString(true);
var placeholders = PlaceholderUtils.getPlaceholders();
if (pAdditionalPlaceholders)
placeholders = placeholders.concat(pAdditionalPlaceholders);
// get all placeholders which matches the placeholder pattern
var foundPlaceholders = content.match(this.options.placeholderRegExp || PlaceholderUtils.getRegexpMatchAll());
if (foundPlaceholders == null)
foundPlaceholders = [];
// clean placeholder from the spechial strings (e.g. to filter '=' in emls)
if (typeof this.options.parsePlaceholderFn === "function")
foundPlaceholders = foundPlaceholders.map(this.options.parsePlaceholderFn);
// filter the possible placeholders by all placeholders found
placeholders = placeholders.filter(function(placeholder)
return foundPlaceholders.includes(placeholder.getFormattedName()) || pForcedPlaceholders.includes(placeholder.getFormattedName());
* Builds an object with the placeholder replacement data for multiple contacts
* @param {Placeholder[]} pAdditionalPlaceholders Additional placeholders that should be used. You can use placeholders with the
* types FIXEDVALUE and CALLBACKFUNCTION if you want to calculate the replacement values yourself.
*
* @return {Object} Object containing the data. The structure is like {contactId : {placeholderName : replacementValue, ...}, ...}
*/
DocumentTemplate.prototype.getReplacementsByContactIds = function (pContactIds, pAdditionalPlaceholders)
var placeholders = this._getRequiredPlaceholders(["{@firstname@}", "{@lastname@}"], pAdditionalPlaceholders);
var contactPlaceholders = [];
var additionalPlaceholders = {};
placeholders.forEach(function (placeholder)
{
switch (placeholder.type)
{
case Placeholder.types.ADDRESSFORMAT:
case Placeholder.types.SQLPART:
case Placeholder.types.SQLPARTFUNCTION:
contactPlaceholders.push(placeholder);
break;
case Placeholder.types.FIXEDVALUE:
case Placeholder.types.CALLBACKFUNCTION:
additionalPlaceholders[placeholder.getFormattedName()] = placeholder;
var contactIdPlaceholder = new Placeholder("contactId", Placeholder.types.SQLPART, "CONTACT.CONTACTID");
contactPlaceholders = [contactIdPlaceholder].concat(contactPlaceholders);
var addressData = getAddressesData(pContactIds, contactPlaceholders, 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];
if (!(contactId in replacements))
replacements[contactId] = {};
placeholderNames.forEach(function (placeholderName, ii)
replacements[contactId][placeholderName] = addressData[i][ii];
});
for (let placeholderName in additionalPlaceholders)
{
var placeholder = additionalPlaceholders[placeholderName];
if (placeholder.type === Placeholder.types.FIXEDVALUE)
replacements[contactId][placeholderName] = placeholder.valueDefinition;
else if (placeholder.type === Placeholder.types.CALLBACKFUNCTION)
replacements[contactId][placeholderName] = placeholder.valueDefinition.call(this, contactId);
}
}
return replacements;
}
/*
* replaces a given Odt-File on the server and returns the replaced base64-file
*
* @param {Object} pReplacements map of placeholders and replacements
* @param {Array} pTableData
*
* @return {String} base64-encoded replaced file
*
* @private
*/
DocumentTemplate.prototype._getReplacedODT = function (pReplacements, pTableData)
var filename = this.filename;
if (!filename)
filename = "dummyname.odt";
//save the file on the server so it can be unzipped via pack.getFromZip
var serverFilePath = vars.get("$sys.servertemp") + "/clientid_" + (vars.exists("$sys.clientid") ? vars.get("$sys.clientid") : 0)
+ "/" + util.getNewUUID() + "/" + filename.replace(/\\/g, "/");
fileIO.storeData(serverFilePath, this.content, util.DATA_BINARY, false);
var replacedFileData = null;
try
{
if (!_replaceODTFile(pReplacements, serverFilePath, pTableData))
return null;
replacedFileData = fileIO.getData(serverFilePath, util.DATA_BINARY);
}
finally
{
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 (!Array.isArray(pReplacements))
pReplacements = [pReplacements];
if (!pTableData)
pTableData = [];
var tablePlaceholders = [];
if (pTableData.length > 0)
{
//pTableData[0] = first document
tablePlaceholders = pTableData[0].map(function (tblData)
{
if (tblData && tblData.length > 0)
return new Set(Object.keys(tblData[0])); //tblData[0] = first row
return new Set();
});
}
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.slice(bodybegin, bodyend);
var fullBody = ""; //body that contains all pages (required when the replacing is done for several contacts)
var beforeBody = contentXml.slice(0, bodybegin);
var afterBody = contentXml.slice(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)
{
S.Listl
committed
currentBody = StringUtils.replaceAll(currentBody, placeholder,
replacements[placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&"), "ig");
//for (let tblIndex = 0; tblIndex < tables.length; tblIndex++) //iterate over all tables in the document
if (tables.length > 0)
let hasMoreTables = currentBody.includes("</table:table>");
for (let tblI = 0; tblI < 10 && hasMoreTables; tblI++)
tableEnd = currentBody.indexOf("</table:table>", tableEnd);
if (tableEnd !== -1) //stop if there is no table
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
tableEnd += 14;
let rowBegin = currentBody.slice(0, tableEnd).lastIndexOf("<table:table-row");
let rowEnd = currentBody.indexOf("</table:table-row>", rowBegin) + 18;
let tableRow = currentBody.slice(rowBegin, rowEnd);
let rowPlaceholders = tableRow.match(PlaceholderUtils.getRegexpMatchAll());
//find the table data that contains all required placeholders
let tableDataIndex = !rowPlaceholders ? -1 : tablePlaceholders.findIndex(function (placeholderSet)
{
return rowPlaceholders.every(function (placeholderName)
{
return placeholderSet.has(placeholderName);
});
});
if (tableDataIndex !== -1)
{
let afterTable = currentBody.slice(rowEnd);
currentBody = currentBody.slice(0, rowBegin);
tableEnd -= tableRow.length;
let tableData = tables[tableDataIndex];
for (let rowIndex = 0; rowIndex < tableData.length; rowIndex++)
{
let tableRowData = tableData[rowIndex];
let replacedRow = that._replaceText(tableRow, tableRowData);
currentBody += replacedRow;
tableEnd += replacedRow.length;
}
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])
{
S.Listl
committed
styles = StringUtils.replaceAll(styles, placeholder,
pReplacements[0][placeholder].replace(/\n/ig, "<text:line-break/>").replace(/&/ig, "&"), "ig");
}
pack.addToZip(pODTFileName, "styles.xml", util.encodeBase64String(styles));
return true;
}
return false;
}
}
/*
* This function is used to replace placeholders via DocXTemplater
*
* @param {Object} pReplacements - Must contain an object, which holds the placeholders
*
* @return {String} returns the modified document in a BASE64 coded string
*
* @private
*/
DocumentTemplate.prototype._getReplacedDOCX = function (pReplacements)
var startDelimiter = this.options.startDelimiter;
var endDelimiter = this.options.endDelimiter;
for (let placeholder in pReplacements) //removes the prefix and postfix, the process needs it like this
replacements[placeholder.slice(startDelimiter.length, -endDelimiter.length)] = pReplacements[placeholder];
var documentData = DocxtemplaterUtils.generateDocument(this.content, replacements, startDelimiter, endDelimiter);
return documentData;
}
/**
* functions for working with letters (mails)
*/
function LetterUtils () {}
/**
* opens a new letter
*
* @param {String} pContactId id of the contact to fetch the data from

Benjamin Ulrich
committed
* @param {String} pComingFrom source from where you started (e.g. "PERSON", "ORGANISATION" )

Benjamin Ulrich
committed
LetterUtils.openNewLetter = function (pContactId, pComingFrom)

Benjamin Ulrich
committed
"ContactId_param" : pContactId,
"ComingFrom_param" : pComingFrom
Johannes Goderbauer
committed
neon.openContext("Letter", "LetterEdit_view", null, neon.OPERATINGSTATE_VIEW, 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
* @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)
if (pFileUpload.isFilled() && pText != "")
{
// use fileUpload but use the custom text as bindata
pFileUpload.bindata = util.encodeBase64String(pText);
}
else if (!pFileUpload.isFilled() && pText != "" && pKind == $KeywordRegistry.documentTemplateType$textModular() || pKind == $KeywordRegistry.documentTemplateType$mail()) // edit is only allowed in modular templates
{
pFileUpload.filename = pTemplateName;
// if it is a htmlTemplate save it with the html extension
if (pClassification == $KeywordRegistry.documentTemplateTypeCategory$htmlTemplate())
pFileUpload.fileExtension = "txt";
pFileUpload.bindata = util.encodeBase64String(pText);
}
var fileUploadType = DocumentTemplate.types.fromMimeType(pFileUpload.mimeType);
// treat txt as html for emails
if (pFileUpload.fileExtension == "txt" && pKind == $KeywordRegistry.documentTemplateType$mail())
{
pFileUpload.fileExtension = "html";
// convert text to html
pFileUpload.bindata = util.encodeBase64String(text.text2html(util.decodeBase64String(pFileUpload.bindata), true));
}
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)
if (pFileUpload.isFilled())
{
db.insertBinary("DOCUMENTTEMPLATE", "DOCUMENT", pTemplateId,
"", pFileUpload.bindata, pFileUpload.filename, "", "TEMPLATE", SqlUtils.getBinariesAlias());
}
}
/**
* 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)
if (pFileUpload.isFilled())
{
var assignmentTable = "DOCUMENTTEMPLATE";
var assignmentName= "DOCUMENT";
var keyword = "TEMPLATE";
var binMeta = db.getBinaryMetadata(assignmentTable, assignmentName, pTemplateId, false, SqlUtils.getBinariesAlias(), keyword);

Johannes Hörmann
committed
SingleBinaryUtils.insert(assignmentTable, assignmentName, pTemplateId, pFileUpload.bindata, pFileUpload.filename, null, "TEMPLATE", SqlUtils.getBinariesAlias());
db.updateBinary(binMeta[0][db.BINARY_ID], "", pFileUpload.bindata, pFileUpload.filename, "", keyword, SqlUtils.getBinariesAlias());
}
}
/**
* 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, type] or ["", type] or ["", null]
* Content is only set, if it is HTML, TXT or EML
*/
DocumentTemplateUtils.getTemplateContent = function (pTemplateId, pFileUpload)
{
var type;
var bindata;
if (pFileUpload.isFilled())
{
type = DocumentTemplate.types.fromFileExtension(pFileUpload.fileExtension);
bindata = pFileUpload.bindata
}
else
{
var template = DocumentTemplateUtils.getTemplate(pTemplateId, false);
if (template != null)
{
bindata = template.content;
type = template.type;
}
}