import("system.translate"); import("ActivityTask_lib"); import("system.util"); import("Contact_lib"); import("system.datetime"); import("system.neon"); import("Employee_lib"); import("system.vars"); import("KeywordRegistry_basic"); import("Sql_lib"); import("system.db"); import("DocumentTemplate_lib"); import("Communication_lib"); import("Email_lib"); import("system.process"); import("system.notification"); import("Document_lib"); /** * functions for bulk mails */ function BulkMailUtils () {} /** * Executes a process to send bulk mails on the server and creates a notification when finished. * * @param {String} pBulkMailId id of the bulk mail * @param {Array} [pTestRecipients] overwrite the recipients (e.g. for testing) * @param {String} [pUser=currentUser] User that will get the notification, if null (not undefined!), no notification * will be created. */ BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pTestRecipients, pUser) { if (pUser === undefined) pUser = EmployeeUtils.getCurrentUserId(); process.executeAsync("sendBulkMail_serverProcess", { bulkMailId : pBulkMailId, testRecipients : JSON.stringify(pTestRecipients), user : pUser || "" } , false, pUser, process.THREADPRIORITY_NORM, process.TIMERTYPE_SERVER ); } /** * Sends a bulk mail. You should only call this function on the server because it * can take some time to execute, use BulkMailUtils.sendBulkMailOnServer instead. * * @param {String} pBulkMailId id of the bulk mail * @param {Array} [pTestRecipients] overwrite the recipients (e.g. for testing) * * @return {Object} count of sucessful and failed mails */ BulkMailUtils.sendBulkMail = function (pBulkMailId, pTestRecipients) { var [templateId, subject, emailSender, createActivity, bulkMailName, useTemplateAttachments] = newSelect("DOCUMENTTEMPLATE_ID, SUBJECT, SENDER, CREATEACTIVITIES, NAME, USE_TEMPLATE_ATTACHMENTS") .from("BULKMAIL") .where("BULKMAIL.BULKMAILID", pBulkMailId) .arrayRow(); useTemplateAttachments = useTemplateAttachments == "1"; var template = BulkMailUtils.getBulkMailTemplate(pBulkMailId, templateId, true, useTemplateAttachments); var recipientData; if (pTestRecipients) { recipientData = pTestRecipients.map(function (row) { return ["", row[0], row[1], "", ""]; }); } else { recipientData = newSelect("BULKMAILRECIPIENTID, BULKMAILRECIPIENT.CONTACT_ID, BULKMAILRECIPIENT.EMAIL_ADDRESS, PERSON_ID, ORGANISATION_ID") .from("CONTACT") .join("BULKMAILRECIPIENT", "BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") .where("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) .and("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$sent(), SqlBuilder.NOT_EQUAL()) .and(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)) .table(); } var contactIds = recipientData.map(function (e) {return e[1];}); var successIds = []; var failedIds = []; var sentDate = vars.get("$sys.date"); var mails = template.getReplacedEmailsByContactIds(contactIds); var subjectTemplate = new DocumentTemplate(subject, DocumentTemplate.types.PLAIN); var subjects = subjectTemplate.getReplacedContentByContactIds(contactIds); var bulkMailLink = [["BulkMail", pBulkMailId]]; var activitySubject = translate.withArguments("Bulk mail \"%0\" sent", [bulkMailName]); for (let i = 0, l = recipientData.length; i < l; i++) { let isSuccess = false; let contactId = recipientData[i][1]; let email = mails[contactId]; if (email !== undefined && recipientData[i][2]) { email.toRecipients = [recipientData[i][2]]; email.sender = emailSender; email.subject = subjects[contactId]; isSuccess = email.send(); } if (recipientData[i][0]) //set the recipient status to 'sent' or 'failed' { Array.prototype.push.call(isSuccess ? successIds : failedIds, recipientData[i][0]); if (isSuccess && createActivity == "1") { let activityData = { categoryKeywordId : $KeywordRegistry.activityCategory$mail(), directionKeywordId : $KeywordRegistry.activityDirection$outgoing(), subject : activitySubject, content : email.body }; let contactLink = [[ContactUtils.getContextByPersOrg(recipientData[i][3], recipientData[i][4]), recipientData[i][1]]]; ActivityUtils.insertNewActivity(activityData, bulkMailLink.concat(contactLink)); } } } newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", successIds, SqlBuilder.IN()) .updateData(true, "BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$sent(), sentDate]); newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", failedIds, SqlBuilder.IN()) .updateData(true, "BULKMAILRECIPIENT", ["STATUS", "SENTDATE"], null, [$KeywordRegistry.bulkMailRecipientStatus$failed(), sentDate]); if (!pTestRecipients) //if its just a test run, don't set the status to sent { newWhere("BULKMAIL.BULKMAILID", pBulkMailId) .updateData(true, "BULKMAIL", ["STATUS"], null, [$KeywordRegistry.bulkMailStatus$sent()]); } return { sucessful : successIds.length, failed : failedIds.length }; } /** * opens a context to select a bulk mail to add recipients to * * @param {String[]} pContactIds recipients that should be added */ BulkMailUtils.openAddRecipientView = function (pContactIds) { var params = { "ContactIds_param" : pContactIds }; neon.openContext("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params); } /** * deletes all bulk mail recipients that have a commrestriction for emails * * @param {String} pBulkMailId */ BulkMailUtils.removeCommRestrictionRecipients = function (pBulkMailId) { var recipientIds = newSelect("BULKMAILRECIPIENTID") .from("BULKMAILRECIPIENT") .join("CONTACT", newWhere() .and("BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") .and(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail()))) .where("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) .arrayColumn(); newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", recipientIds, SqlBuilder.IN()) .deleteData(); } /** * adds recipients to a bulkmail * * @param {String} pBulkMailId bulk mail id * @param {String[]} pContactIds contact ids of the recipients */ BulkMailUtils.addRecipients = function (pBulkMailId, pContactIds) { var columns = [ "BULKMAILRECIPIENTID", "BULKMAIL_ID", "CONTACT_ID", "STATUS", "EMAIL_ADDRESS" ]; if(pContactIds.length > 0) { var contactData = newSelect(["CONTACTID", "(" + CommUtil.getStandardSubSqlMail(newWhere("COMMUNICATION.CONTACT_ID = CONTACTID")) + ")"]) .from("CONTACT") .where("CONTACT.CONTACTID", pContactIds, SqlBuilder.IN()) .table(); var inserts = contactData.map(function(pContact) { //TODO: get columntype for better performance outside loop return ["BULKMAILRECIPIENT", columns, null, [util.getNewUUID(), pBulkMailId, pContact[0], $KeywordRegistry.bulkMailRecipientStatus$pending(), pContact[1]]]; }); db.inserts(inserts); } } /** * Loads the document template of a bulk mail. If the bulk mail itself has a * template, it is preferred over the documentTemplate-id. * * @param {String} pBulkMailId bulkmail id * @param {String} pDocumentTemplateId documentTemplate id * @param {Boolean} [pResolveSubtemplates=true] if true subtemplates are resolved (if the type is html) * @param {Boolean} [pUseTemplateAttachments=false] if true the attachments from the document template is always used * @param {FileUpload} [pUpload] the upload value if a custom template is used * * @return {DocumentTemplate} the document template, null if no content was found. */ BulkMailUtils.getBulkMailTemplate = function (pBulkMailId, pDocumentTemplateId, pResolveSubtemplates, pUseTemplateAttachments, pUpload) { if (pUpload.isFilled() && BulkMailUtils.isValidMimeType(pUpload.mimeType)) return DocumentTemplate.fromUpload(pUpload); var bulkTemplate = DocumentTemplate.loadTemplate(pBulkMailId, "BULKMAIL", pResolveSubtemplates); var documentTemplate = DocumentTemplate.loadTemplate(pDocumentTemplateId, undefined, pResolveSubtemplates); if (!bulkTemplate.content) { return documentTemplate; } else { if (pUseTemplateAttachments) bulkTemplate.setAttachments(documentTemplate.getAttachments()); return bulkTemplate; } } /** * checks if a contact is a recipient of a bulk mail * * @param {String} pBulkMailId bulkmail id * @param {String} pContactId contact id * @param {String} pRecipientId bulkmailrecipient id * @return {boolean} true, if the contact is a recipient */ BulkMailUtils.isRecipient = function (pBulkMailId, pContactId, pRecipientId) { return newSelect("count(*)") .from("BULKMAILRECIPIENT") .where("BULKMAILRECIPIENT.CONTACT_ID", pContactId) .and("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) .andIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", pRecipientId, SqlBuilder.NOT_EQUAL()) .cell() != "0"; //TODO: is there a way exists could be used? } /** * opens the BulkMail context in new mode * * @param {String[]} [pRecipients] recipients that should be added after creation */ BulkMailUtils.newBulkMail = function (pRecipients) { var params = { "PresetRecipients_param" : JSON.stringify(pRecipients) }; neon.openContext("BulkMail", "BulkMailEdit_view", null, neon.OPERATINGSTATE_NEW, params); } /** * Filters the given contactIds if they can be added as new recipients. * Checks if a contact is already a recipient or if there is a advertising ban. * * @param {String} pBulkMailId id of the bulk mail the contacts should be added to * @param {String[]} pContactIds contacts to filter * @return {String[]} contacts that can be added as recipients */ BulkMailUtils.filterNewRecipients = function (pBulkMailId, pContactIds) { return newSelect("CONTACTID") .from("CONTACT") .whereIfSet("CONTACT.CONTACTID", pContactIds, SqlBuilder.IN()) // only add contacts that aren't already recipients .and(null, newSelect("BULKMAILRECIPIENTID") .from("BULKMAILRECIPIENT") .where("BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID") .and("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId) , SqlBuilder.NOT_EXISTS()) // check if there's a commrestriction .and(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$mail(), true)) .arrayColumn(); } /** * opens the given bulk mail */ BulkMailUtils.openBulkMail = function (pBulkMailId) { neon.openContext("BulkMail", "BulkMailMain_view", [pBulkMailId], neon.OPERATINGSTATE_VIEW, null); } /** * checks is the given mime type can be used for a bulk mail * * @param {String} pMimeType mime type * @return {Boolean} wheter the type is usable or not */ BulkMailUtils.isValidMimeType = function (pMimeType) { var templateType = DocumentTemplate.types.fromMimeType(pMimeType); return BulkMailUtils.isValidTemplateType(templateType) } /** * checks is the given template type can be used for a bulk mail * * @param {String} pTemplateType template type * @return {Boolean} wheter the type is usable or not */ BulkMailUtils.isValidTemplateType = function (pTemplateType) { switch (pTemplateType) { case DocumentTemplate.types.EML: case DocumentTemplate.types.HTML: case DocumentTemplate.types.TXT: return true; default: return false; } } /** * @param {String} pStatus the keyid of the current status * @return {Boolean} true if the status is "sent" or "sending" */ BulkMailUtils.isStatusSendingOrSent = function (pStatus) { return pStatus == $KeywordRegistry.bulkMailStatus$sent() || pStatus == $KeywordRegistry.bulkMailStatus$beingSent() } BulkMailUtils.copy = function(pBulkMailId) { var params = { "CopyBulkMailId_param" : pBulkMailId }; neon.openContext("BulkMail", null, null, neon.OPERATINGSTATE_NEW, params); } function SerialLetterUtils () {} /** * adds recipients to a serial letter * * @param {String} pSerialLetterId serial letter id * @param {String[]} pContactIds contact ids of the recipients */ SerialLetterUtils.addRecipients = function (pSerialLetterId, pContactIds) { var columns = [ "LETTERRECIPIENTID", "SERIALLETTER_ID", "CONTACT_ID" ]; var inserts = []; for (let i = 0, l = pContactIds.length; i < l; i++) { inserts.push(["LETTERRECIPIENT", columns, null, [util.getNewUUID(), pSerialLetterId, pContactIds[i]]]); } db.inserts(inserts); } /** * opens a context to select a serial letter to add recipients to * * @param {String[]} pContactIds recipients that should be added */ SerialLetterUtils.openAddRecipientView = function (pContactIds) { var params = { "ContactIds_param" : pContactIds }; neon.openContext("SerialLetterAddRecipients", "SerialLetterAddRecipientsEdit_view", null, neon.OPERATINGSTATE_NEW, params); } /** * executes a server process that builds a serial letter * * @param {String} pSerialLetterId serial letter id * @param {String[]} [pRecipientIds] Letter recipient ids of that should be used. * If omitted, all recipients of the letter will be used. */ SerialLetterUtils.buildSerialLetterOnServer = function (pSerialLetterId, pRecipientIds) { var user = EmployeeUtils.getCurrentUserId(); process.executeAsync("buildSerialLetter_serverProcess", { "serialLetterId" : pSerialLetterId, "recipientIds" : JSON.stringify(pRecipientIds), "user" : user } , false, vars.get("$sys.user"), process.THREADPRIORITY_NORM, process.TIMERTYPE_SERVER); } /** * executes a server process that builds a serial letter * * @param {String} pSerialLetterId serial letter id * @param {String[]} [pRecipientIds] Letter recipient ids of that should be used. * If omitted, all recipients of the letter will be used. */ SerialLetterUtils.buildSerialLetter = function (pSerialLetterId, pRecipientIds) { var [templateId, title] = newSelect("DOCUMENTTEMPLATE_ID, TITLE") .from("SERIALLETTER") .where("SERIALLETTER.SERIALLETTERID", pSerialLetterId) .arrayRow(true); var template = SerialLetterUtils.getSerialLetterTemplate(pSerialLetterId, templateId); var contactIdsSelect = newSelect("CONTACT_ID") .from("LETTERRECIPIENT") .join("CONTACT", newWhere("LETTERRECIPIENT.CONTACT_ID = CONTACT.CONTACTID")) .where("LETTERRECIPIENT.SERIALLETTER_ID", pSerialLetterId) .andIfSet(ContactUtils.getCommRestrictionCondition($KeywordRegistry.communicationMediumCampaign$letter(), true)); if (pRecipientIds && pRecipientIds.length > 0) contactIdsSelect.and("LETTERRECIPIENT.LETTERRECIPIENTID", pRecipientIds, SqlBuilder.IN()); var contactIds = contactIdsSelect.table(); if(template != null){ return { content : template.getSerialLetterByContactIds(contactIds), filename : template.filename, title : title };} else{ return{ title : title } } } /** * checks if a contact is a recipient of a serial letter * * @param {String} pSerialLetterId serial letter id * @param {String} pContactId contact id * @param {String} [pRecipientId] letter recipient id * @return {boolean} true, if the contact is a recipient */ SerialLetterUtils.isRecipient = function (pSerialLetterId, pContactId, pRecipientId) { return newSelect("count(*)") .from("LETTERRECIPIENT") .where("LETTERRECIPIENT.CONTACT_ID", pContactId) .and("LETTERRECIPIENT.SERIALLETTER_ID", pSerialLetterId) .andIfSet("LETTERRECIPIENT.LETTERRECIPIENTID", pRecipientId, SqlBuilder.NOT_EQUAL()) .cell() != "0"; // TODO: is there a way exists could be used? } SerialLetterUtils.openSerialLetter = function (pSerialLetterId) { neon.openContext("SerialLetter", "SerialLetterMain_view", [pSerialLetterId], neon.OPERATINGSTATE_VIEW, null); } /** * Loads the document template of a serial letter. If the serial letter itself has a * template, it is preferred over the documentTemplate-id. * * @param {String} pLetterId serial letter id * @param {String} pDocumentTemplateId documentTemplate id * * @return {DocumentTemplate} the document template */ SerialLetterUtils.getSerialLetterTemplate = function (pLetterId, pDocumentTemplateId) { var template = DocumentTemplate.loadTemplate(pLetterId, "SERIALLETTER"); if (!template.type) template = DocumentTemplate.loadTemplate(pDocumentTemplateId); return template; }