Skip to content
Snippets Groups Projects
process.js 58.3 KiB
Newer Older
Pascal Neub's avatar
Pascal Neub committed
import("system.neonFilter");
import("CommunicationBlacklist_lib");
import("EmailFilterHandling_lib");
import("system.logging");
import("system.entities");
import("MarketingCondition_lib");
Johannes Hörmann's avatar
Johannes Hörmann committed
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");
import("system.tools");
import("FileUtil_lib");
Johannes Hörmann's avatar
Johannes Hörmann committed

/**
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
function BulkMailUtils () {}

/**
 * Executes a process to send bulk mails on the server and creates a notification when finished.
 * 
 * @param {String} pBulkMailId                      <p>
 *                                                  Id of the bulk mail.<br>
 * @param {Bool} pTestRun (optional)                <p>
 *                                                  True indicates a Testrun<br>
 * @param {String} pUser=currentUser (optional)     <p>
 *                                                  User that will get the notification, <br>
 *                                                  if null (not undefined!), no notification<br>
 *                                                  will be created.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.sendBulkMailOnServer = function (pBulkMailId, pTestRun, pUser)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    if (pUser === undefined)
        pUser = EmployeeUtils.getCurrentUserId();
    
    var processConfig = process.createStartAsyncConfig()
        .setName("sendBulkMail_serverProcess")
        .setLocalVariables({
Johannes Hörmann's avatar
Johannes Hörmann committed
            bulkMailId : pBulkMailId,
Johannes Hörmann's avatar
Johannes Hörmann committed
            user : pUser || ""
    process.startAsync(processConfig);
Johannes Hörmann's avatar
Johannes Hörmann committed
}

/**
 * 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                  <p>
 *                                              Id of the bulk mail.<br>
 * @param {Bool} pIsTestRun (optional)            <p>
 * @param {Bool} pUser (optional)               <p>
 *                                              If there are no test recipients or no recipients marked for a test replacement in a test run 
Martin Groppe's avatar
Martin Groppe committed
 *                                              we send an email to this user instead<br>
 * @param {Object} pAdditionalLinkParameters(optional)<p>
 *                                              Additional parameters that get put into the weblinks for the redirect webservice.
 *                                              Expects object  of key value pairs. <br>   
 * @param {String} pAdHochMailingRecipientId (optional)     <p>
 *                                              The id of a recipient added to a adhoc mailing list. The mailing ignores all other recipients if this parameter is set<br>                                                                                                                                          
 * @param {String} pOriginUrl (optional)        <p>
 *                                              Base URL for link replacement. Only needed when sending an Ad-Hoc Mailing without a configured baseReplacementURL in the Configuration <br>
 *                                              since sys.origin does not exist during workflowactions.
 * @return {Object}                             <p>
 *                                              Count of sucessful and failed mails.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
Martin Groppe's avatar
Martin Groppe committed
BulkMailUtils.sendBulkMail = function (pBulkMailId, pIsTestRun, pUser, pAdditionalLinkParameters, pAdHochMailingRecipientId, pOriginUrl)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    if (pIsTestRun == undefined)
    {
        pIsTestRun = false;
    }
    var sendUserTitle = project.getInstanceConfigValue("custom.bulkmail.user");
    var [templateId, subject, emailSender, createActivity, bulkMailName, useTemplateAttachments, mosaicoTemplateId] = 
                newSelect("DOCUMENTTEMPLATE_ID, SUBJECT, SENDER_EMAIL_ADDRESS, CREATEACTIVITIES, NAME, USE_TEMPLATE_ATTACHMENTS, MOSAICOTEMPLATE_ID")
                    .from("BULKMAIL")
                    .where("BULKMAIL.BULKMAILID", pBulkMailId)
                    .arrayRow();

    useTemplateAttachments = Utils.toBoolean(useTemplateAttachments);
    var template = BulkMailUtils.getBulkMailTemplate(pBulkMailId, templateId, true, useTemplateAttachments, null, mosaicoTemplateId);
Johannes Hörmann's avatar
Johannes Hörmann committed
    var recipientData;
    var recipientLoadConfig = entities.createConfigForLoadingRows()
Martin Groppe's avatar
Martin Groppe committed
                .fields(["BULKMAILRECIPIENTID", "CONTACT_ID", "EMAIL_ADDRESS", "PERSON_ID", "ORGANISATION_ID", "HASCOMMUNICATIONREJECTION"])
Martin Groppe's avatar
Martin Groppe committed
        .entity("BulkMailRecipient_entity")
        .provider("RecipientsToBeMailed")
        .addParameter("BulkMailId_param", pBulkMailId)
        .addParameter("IsTestMail_param", pIsTestRun);
    
Martin Groppe's avatar
Martin Groppe committed
    if(pAdHochMailingRecipientId)
    {
        recipientLoadConfig.uid(pAdHochMailingRecipientId);
    }
    
    recipientData = entities.getRows(recipientLoadConfig);
    var blacklist = new CommunicationBlacklist().loadBlacklistRecipients(pBulkMailId);
    
    if (pIsTestRun)
Johannes Hörmann's avatar
Johannes Hörmann committed
    {
        testRecipientData = newSelect("BULKMAILTESTRECIPIENT.CONTACT_ID, BULKMAILTESTRECIPIENT.EMAIL_ADDRESS")
            .from("BULKMAILTESTRECIPIENT")
            .where("BULKMAILTESTRECIPIENT.BULKMAIL_ID", pBulkMailId)
            
        if (testRecipientData.length == 0 || recipientData.length == 0 && pUser)
        {
            var userData = tools.getUserByAttribute(tools.NAME,pUser,tools.PROFILE_DEFAULT);

            if (userData)
            {
                testRecipientData = [userData[tools.PARAMS][tools.CONTACTID],userData[tools.PARAMS][tools.EMAIL]];
                recipientData = [{"CONTACT_ID":userData[tools.PARAMS][tools.CONTACTID],"EMAIL":userData[tools.PARAMS][tools.EMAIL]}];
            }
        }
Johannes Hörmann's avatar
Johannes Hörmann committed
    }
    new SqlBuilder()
        .tableName("MAIL_RUN")
        .insertFields({
            "MAIL_RUNID": mailrunId,
            "OBJECT_ROWID": pBulkMailId,
            "OBJECT_TYPE": "Bulkmail",
            "DATE_RUN_START": vars.get("$sys.date"),
            "STATUS": $KeywordRegistry.bulkMailStatus$beingSent(),
            "TESTRUN": pIsTestRun ? 1 : 0
        });
Sebastian Listl's avatar
Sebastian Listl committed
    var mailLogIds = new Map();
    var contactIds = recipientData.map(function (recipient) 
    {
        var contactId = recipient["CONTACT_ID"]; 
        mailLogIds.set(contactId, util.getNewUUID());
        return contactId;
    });
    
Martin Groppe's avatar
Martin Groppe committed
    var baseUrl = (pOriginUrl || project.getInstanceConfigValue("custom.bulkmail.baseReplacementURL", vars.get("$sys.origin"))) + "/services/rest/redirect_rest?";
    
    var additionalParameterString = "";
    
    if (pAdditionalLinkParameters)
    {
        additionalParameterString = "&" + Object.keys(pAdditionalLinkParameters)
        .map(function (key)
        {
            return key + "=" + pAdditionalLinkParameters[key]
        })
        .join("&");
    }
    
    
Sebastian Listl's avatar
Sebastian Listl committed
    var linkPlaceholders = newSelect(["PLACEHOLDER", "WEBLINKID", "URL", "ISREDIRECT"])
        .from("WEBLINK")
        .table()
        .map(function ([placeholder, weblinkId, url, isRedirect])
        {
Sebastian Listl's avatar
Sebastian Listl committed
            if (Utils.toBoolean(isRedirect))
            {
Martin Groppe's avatar
Martin Groppe committed
                        return baseUrl + "link=" + weblinkId + "&log=" + mailLogIds.get(pContactId) + additionalParameterString;
Sebastian Listl's avatar
Sebastian Listl committed
                return new Placeholder(placeholder, Placeholder.types.CALLBACKFUNCTION, linkFn);
            }
            else
            {
                linkFn = function (pContactId)
                    {
                        return StringUtils.replaceAll(url, "{@contactid@}", pContactId);
                    }  
                return new Placeholder(placeholder, Placeholder.types.CALLBACKFUNCTION, linkFn);
            }
Martin Groppe's avatar
Martin Groppe committed
            return (pOriginUrl || project.getInstanceConfigValue("custom.bulkmail.baseReplacementURL", vars.get("$sys.origin"))) + "/services/rest/webview_rest?" + "log=" + mailLogIds.get(pContactId);
        }
    var webviewPlaceholder = new Placeholder("webview", Placeholder.types.CALLBACKFUNCTION, webviewFn);   
    
    var additionalPlaceholders = [webviewPlaceholder].concat(linkPlaceholders);
    
Johannes Hörmann's avatar
Johannes Hörmann committed
    var successIds = [];
    var failedIds = [];
    var bouncedSoftIds = [];
    var bouncedHardIds = [];
Johannes Hörmann's avatar
Johannes Hörmann committed
    var sentDate = vars.get("$sys.date");
    var mails = template.getReplacedEmailsByContactIds(contactIds, additionalPlaceholders);
Johannes Hörmann's avatar
Johannes Hörmann committed
    
    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]);
    
    var emailFilterProcessor = new IncomingEmailFilterProcessor().loadFilters();
    
    if (!pIsTestRun)
    {
        recipientData.forEach(function (recipient)
        {
            let isSuccess = false;
            let bouncedStatus = null;
            let recipientId = recipient["BULKMAILRECIPIENTID"];
            let contactId = recipient["CONTACT_ID"];
            let emailAddress = recipient["EMAIL_ADDRESS"];
            let personId = recipient["PERSON_ID"];
            let organisationId = recipient["ORGANISATION_ID"];
                let hasCommunicationRejection = Utils.toBoolean(recipient["HASCOMMUNICATIONREJECTION"]);
            let email = mails[contactId];
Sebastian Listl's avatar
Sebastian Listl committed
            let mailLogId = mailLogIds.get(contactId);
            let recipientStatus = $KeywordRegistry.bulkMailRecipientStatus$failed();
                if (email !== undefined && emailAddress && !blacklist.hasContactId(contactId) && !hasCommunicationRejection)
                try
                {
                    email.toRecipients = [emailAddress];
                    email.sender = emailSender;
                    email.subject = subjects[contactId];

                    BulkMailUtils.storeEmlFile(pBulkMailId, mailrunId, mailLogId,email.getEML());
                    isSuccess = email.send(sendUserTitle);
                    if (!isSuccess)
                    {
                        errorMessage = logging.toLogString(email.getMailError(), true);
                        var filterType = emailFilterProcessor.processError(errorMessage, contactId, emailAddress);
                        if (filterType == $KeywordRegistry.emailFilterType$bounceHard())
                            bouncedStatus = $KeywordRegistry.bulkMailRecipientStatus$hardBounce();
                        else if (filterType == $KeywordRegistry.emailFilterType$bounceSoft())
                            bouncedStatus = $KeywordRegistry.bulkMailRecipientStatus$softBounce();
                        recipientStatus = bouncedStatus || $KeywordRegistry.bulkMailRecipientStatus$failed();
                    }
                    else
                    {
                        recipientStatus = $KeywordRegistry.bulkMailRecipientStatus$sent();
                    }
                }
                catch (ex)
Johannes Hörmann's avatar
Johannes Hörmann committed
            }

            //set the recipient status to 'sent' or 'failed'
            new SqlBuilder()
                .tableName("MAIL_LOG")
                .insertFields({
                    "MAIL_LOGID": mailLogId,
                    "MAIL_RUN_ID": mailrunId,
                    "CONTACT_ID": contactId,
                    "STATUS": recipientStatus,
                    "SENDER_EMAIL": emailSender,
                    "RECIPIENT_EMAIL": emailAddress,
                    "MAILING_SUBJECT": subjects[contactId],
                    "DATE_SEND": vars.get("$sys.date")
                });
            //TODO: Klären was von alter Logik noch bleiben soll. Status macht nur Sinn wenn jede Bulkmail nur einmal gesendet wird. Bleiben Activitys?
            if (isSuccess)
            {
            }
            else if (bouncedStatus == $KeywordRegistry.bulkMailRecipientStatus$softBounce())
            {
                bouncedSoftIds.push(recipientId);
            }
            else if (bouncedStatus == $KeywordRegistry.bulkMailRecipientStatus$hardBounce())
            {
                bouncedHardIds.push(recipientId);
            }
            else
            {
                failedIds.push(recipientId);
            }
            if (isSuccess && createActivity == "1")
            {
                let activityData = {
                    categoryKeywordId : $KeywordRegistry.activityCategory$mail(),
                    directionKeywordId : $KeywordRegistry.activityDirection$outgoing(),
                    subject : activitySubject,
                    content : email.body
                };
                let contactLink = [[ContactUtils.getContextByPersOrg(personId, organisationId), contactId]];
                ActivityUtils.insertNewActivity(activityData, bulkMailLink.concat(contactLink));
            }
        });
        newWhere("MAIL_RUN.MAIL_RUNID", mailrunId)
            .updateFields({
                "STATUS": $KeywordRegistry.bulkMailStatus$sent(),
                "DATE_RUN_FINISHED": vars.get("$sys.date")
            });

Martin Groppe's avatar
Martin Groppe committed
        if(!pAdHochMailingRecipientId)
        {
         newWhere("BULKMAIL.BULKMAILID", pBulkMailId)
            .updateFields({
                "STATUS": $KeywordRegistry.bulkMailStatus$sent()
    }
    else
    {
        for (let i = 0, l = recipientData.length; i < l; i++)
        {
            
            let isSuccess = false;
            let contactId = recipientData[i]["CONTACT_ID"];
            let email = mails[contactId];
            let currentMailLogId = mailLogIds.get(contactId);

            if (email !== undefined)
            {
                email.sender = emailSender;
                email.subject = "Test: "+subjects[contactId];
                for (let j =0; j<testRecipientData.length;j++)
                {
                        try
                        {
                            email.toRecipients = [testRecipientData[j][1]];
                            email.body = StringUtils.replaceAll(email.body,currentMailLogId,nextMailLogId);
                            currentMailLogId = nextMailLogId;
                            this.storeEmlFile(pBulkMailId, mailrunId, nextMailLogId,email.getEML());
                            isSuccess = email.send(sendUserTitle);
                        }
                        catch(ex)
                        {
                            errorMessage = logging.toLogString(ex, true);
                        }
                        Array.prototype.push.call(isSuccess ? successIds : failedIds, recipientData[i]["BULKMAILRECIPIENTID"]);

                        if (testRecipientData[j][0])
                        {
                            new SqlBuilder()
                            .tableName("MAIL_LOG")
                            .insertFields({
                                "MAIL_LOGID":nextMailLogId,
                                "MAIL_RUN_ID":mailrunId,
                                "CONTACT_ID":testRecipientData[j][0],
                                "ERRORMESSAGE": errorMessage || logging.toLogString(email.getMailError(), true) || "",
                                "STATUS":(isSuccess ?$KeywordRegistry.bulkMailRecipientStatus$sent(): $KeywordRegistry.bulkMailRecipientStatus$failed()),
                                "SENDER_EMAIL":emailSender,
                                "RECIPIENT_EMAIL":testRecipientData[j][1],
                                "MAILING_SUBJECT":email.subject,
                                "DATE_SEND":vars.get("$sys.date")
        newWhere("MAIL_RUN.MAIL_RUNID",mailrunId)
            .updateData(true,"MAIL_RUN",["STATUS","DATE_RUN_FINISHED"],null,[$KeywordRegistry.bulkMailStatus$sent(),vars.get("$sys.date")]);
    }
        newWhere("MAIL_RUN.MAIL_RUNID",mailrunId)
                .updateData(true,"MAIL_RUN",["STATUS","DATE_RUN_FINISHED"],null,[$KeywordRegistry.bulkMailStatus$failed(),vars.get("$sys.date")]);
        if(!pIsTestRun)
        {
            newWhere("BULKMAIL.BULKMAILID", pBulkMailId)
                    .updateFields({
                        "STATUS": $KeywordRegistry.bulkMailStatus$failed()
                    });  
        }
    }
    finally
    {
        if(successIds && failedIds && bouncedSoftIds && bouncedHardIds)
        {
            var updates = [];
            updates = updates.concat(successIds.map(function (successId)
            {
                return  newWhere("BULKMAILRECIPIENT.BULKMAILRECIPIENTID",successId)
                        .buildUpdateStatement({
                            "STATUS": $KeywordRegistry.bulkMailRecipientStatus$sent(),
                            "SENTDATE": sentDate
                        });
            }));

            updates = updates.concat(failedIds.map(function (failedId)
            {
                return  newWhere("BULKMAILRECIPIENT.BULKMAILRECIPIENTID",failedId)
                        .buildUpdateStatement({
                            "STATUS": $KeywordRegistry.bulkMailRecipientStatus$failed(),
                            "SENTDATE": sentDate
                        });
            }));

            updates = updates.concat(bouncedSoftIds.map(function (bouncedSoftId)
            {
                return  newWhere("BULKMAILRECIPIENT.BULKMAILRECIPIENTID",bouncedSoftId)
                        .buildUpdateStatement({
                            "STATUS": $KeywordRegistry.bulkMailRecipientStatus$softBounce(),
                            "SENTDATE": sentDate
                        });
            }));
            updates = updates.concat(bouncedHardIds.map(function (bouncedHardId)
            {
                return  newWhere("BULKMAILRECIPIENT.BULKMAILRECIPIENTID",bouncedHardId)
                        .buildUpdateStatement({
                            "STATUS": $KeywordRegistry.bulkMailRecipientStatus$hardBounce(),
                            "SENTDATE": sentDate
                        });
            }));

            db.updates(updates);
Johannes Hörmann's avatar
Johannes Hörmann committed
    return {
        sucessful : successIds.length,
        failed : failedIds.length
    };
}
Martin Groppe's avatar
Martin Groppe committed
    }   
 * Opens a context to select a bulk mail to add recipients to.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String} pContext             the context of the contacts (Person or Organisation)
 * @param {String[]} pIds               Ids that should be added.<br>
 * @param {String|Object} pFilter       the filter for the contacts that should be used if no Ids are selected
 * @param {String|Object} pParameters     the relevant parameters that are needed to get the Ids from entities.loadrows in the form:{parametername:parametervalue}
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.openAddRecipientView = function (pContext, pIds, pFilter, pParameters)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    if (!Utils.isString(pParameters))
        pParameters = JSON.stringify(pParameters);
    if (!Utils.isString(pIds))
        pIds = JSON.stringify(pIds);
    if (Utils.isString(pFilter))
        pFilter = JSON.parse(pFilter);
    if(Utils.isNullOrEmpty(pFilter.filter))
        pFilter.filter= JSON.parse(JditoFilterUtils.getEmptyFilter()).filter;
    
Pascal Neub's avatar
Pascal Neub committed
    pFilter = JSON.stringify(pFilter);
    var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters({
        "ObjectType_param": pContext,
        "Ids_param": pIds,
        "Filter_param": pFilter,
        "Parameters_param": pParameters
Pascal Neub's avatar
Pascal Neub committed
    }).toString();
    neon.openContextWithRecipe("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", recipe, neon.OPERATINGSTATE_VIEW);
/**
 * Opens a context to select a bulk mail to add recipients to.<br>
 * 
 * @param {String} pRecordsRecipe       RecordsRecipe for the selection that should be added
  */
BulkMailUtils.openAddRecipientViewWithRecipe = function (pRecordsRecipe)
{
    logging.log(pRecordsRecipe);

    var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters({
        "RecordsRecipe_param": pRecordsRecipe
    }).toString();
    neon.openContextWithRecipe("BulkMailAddRecipients", "BulkMailAddRecipientsEdit_view", recipe, neon.OPERATINGSTATE_VIEW);
}

Johannes Hörmann's avatar
Johannes Hörmann committed
/**
 * Deletes all bulk mail recipients that have a commrestriction for emails.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String} pBulkMailId          <p>
 *                                      The mail id.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.removeCommRestrictionRecipients = function (pBulkMailId)
{
    var recipientIds = newSelect("BULKMAILRECIPIENTID")
Johannes Hörmann's avatar
Johannes Hörmann committed
        .from("BULKMAILRECIPIENT")
        .join("CONTACT", "BULKMAILRECIPIENT.CONTACT_ID = CONTACT.CONTACTID")
        .where("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)
        .and(new CommunicationSettingsCondition()
            .emails("BULKMAILRECIPIENT.EMAIL_ADDRESS")
            .rejected()
            .existSettings()
            .buildCondition())
S.Listl's avatar
S.Listl committed
    newWhereIfSet("BULKMAILRECIPIENT.BULKMAILRECIPIENTID", recipientIds, SqlBuilder.IN())
        .deleteData();
 * Adds recipients to a bulkmail.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String} pBulkMailId              <p>
 *                                          Bulk mail id.<br>
 * @param {String[]} pContactIds            <p>
 *                                          Contact ids of the recipients.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.addRecipients = function (pBulkMailId, pContactIds)
{
    if (pContactIds.length > 0)
Johannes Hörmann's avatar
Johannes Hörmann committed
    {
        var sqlBuilder = new SqlBuilder();
        while (pContactIds.length > 0){
            var ContactIdsChunk = pContactIds.splice(0, 1000);            
            contactData = newSelect(["CONTACTID", "(" + CommUtil.getStandardSubSqlMail(newWhere("COMMUNICATION.OBJECT_ROWID = CONTACTID").and("COMMUNICATION.OBJECT_TYPE", "Contact")) + ")"])
                .from("CONTACT")
                .where("CONTACT.CONTACTID", ContactIdsChunk, SqlBuilder.IN())
                .table();
            inserts = inserts.concat(contactData.map(function([contactId, standardMail])
                {
                    return sqlBuilder.buildInsertStatement({
                        "BULKMAIL_ID": pBulkMailId,
                        "CONTACT_ID": contactId,
                        "STATUS": $KeywordRegistry.bulkMailRecipientStatus$added(),
                        "IS_TEST_RECIPIENT" : 0,
                        "EMAIL_ADDRESS": standardMail
                    },  "BULKMAILRECIPIENT", "BULKMAILRECIPIENTID");
                }));
        }
        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.
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String} pBulkMailId                                  <p>
 *                                                              The id of the bulk mail.<br>
 * @param {String} pDocumentTemplateId                          <p>
 *                                                              The id of the document template.<br>
 * @param {Boolean} pResolveSubtemplates=true (optional)        <p>
 *                                                              If true subtemplates are resolved (if the type is html)
 * @param {Boolean} pUseTemplateAttachments=false               <p>
 *                                                              If true the attachments from the document template is always used
 * @param {FileUpload} pUpload (optional)                       <p>
 *                                                              The upload value if a custom template is used.<br>
 * @param {String} pMosaicoTemplateId  (optional)               <p>
 *                                                              The id of the mosaico template.<br>
 * @return {DocumentTemplate}                                   <p>
 *                                                              The document template, null if no content was found.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.getBulkMailTemplate = function (pBulkMailId, pDocumentTemplateId, pResolveSubtemplates, pUseTemplateAttachments, pUpload, pMosaicoTemplateId)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
S.Listl's avatar
S.Listl committed
    if (pUpload && pUpload.isFilled() && BulkMailUtils.isValidMimeType(pUpload.mimeType))
        return DocumentTemplate.fromUpload(pUpload);
    var bulkTemplate = DocumentTemplate.loadTemplate(pBulkMailId, "BULKMAIL", pResolveSubtemplates);
    var documentTemplate = DocumentTemplate.loadTemplate(pDocumentTemplateId, undefined, pResolveSubtemplates);
    var mosaicoTemplate = DocumentTemplate.loadTemplate(pMosaicoTemplateId, "MOSAICOTEMPLATE", pResolveSubtemplates);
    if (!bulkTemplate.content)
    {
        if (pMosaicoTemplateId)
        {    
            return mosaicoTemplate;
        }
        else 
        {    
            return documentTemplate;
        }
        if (pUseTemplateAttachments)
            bulkTemplate.setAttachments(documentTemplate.getAttachments());
        return bulkTemplate;
    }
 * Checks if a contact is a recipient of a bulk mail.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String} pBulkMailId                      <p>
 *                                                  The id of the bulk mail.<br>
 * @param {String} pContactId                       <p>
 *                                                  The contact id.<br>
 * @param {String} pRecipientId                     <p>
 *                                                  The contact id of the contact where,<br>
 *                                                  the bulk mail shall sent to.<br>
 * @return {boolean}                                <p>
 *                                                  True, if the contact is a recipient.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
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?
/**
 * Checks if a bulk mail still has pending recipients.<br>
 * 
 * @param {String} pBulkMailId                      <p>
 *                                                  The id of the bulk mail.<br>
 * @return {boolean}                                <p>
 *                                                  True, if the contact is a recipient.<br>
 */
BulkMailUtils.hasPendingRecipient = function (pBulkMailId)
{
    return new SqlBuilder().selectCount()
                .from("BULKMAILRECIPIENT")
                .where("BULKMAILRECIPIENT.STATUS", $KeywordRegistry.bulkMailRecipientStatus$pending())
                .and("BULKMAILRECIPIENT.BULKMAIL_ID", pBulkMailId)
                .cell() != "0";
}


Johannes Hörmann's avatar
Johannes Hörmann committed
/**
 * Opens the BulkMail context in new mode.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String[]} pRecipients (optional)         <p>
 *                                                  Recipients that should be added after creation.<br>
 * @param {String}   pContext    (optional)         <p>
 *                                                  Context the filter is coming from.<br>  
 * @param {Object} pFilter (optional)               <p>
 *                                                  sys.filter of selection that should be added to new bulkmail<br>                                                                                                
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.newBulkMail = function (pRecipients, pContext, pFilter)
Johannes Hörmann's avatar
Johannes Hörmann committed
{
    var params = {
        "PresetRecipients_param" : pRecipients?JSON.stringify(pRecipients):null,
        "PresetRecipientsContext_param": pContext,
        "PresetRecipientsFilter_param": pFilter?JSON.stringify(pFilter):null
Johannes Hörmann's avatar
Johannes Hörmann committed
    };
Pascal Neub's avatar
Pascal Neub committed
    var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters(params).toString();
    neon.openContextWithRecipe("BulkMail", "BulkMailEdit_view", recipe, neon.OPERATINGSTATE_NEW);
/**
 * Opens the BulkMail context in new mode.<br>
 * 
 * @param {String}   pRecordsRecipe            <p>
 *                                                  Recordsrecipe containing the recipients for the new Bulkmail.
 *                                                  Currently supported Contexts are campaignstep, campaignparticipant, person and <br>                                                  sys.filter of selection that should be added to new bulkmail<br>                                                                                                
 */
BulkMailUtils.newBulkMailWithRecordsRecipe = function (pRecordsRecipe)
{
    if (!Utils.isString(pRecordsRecipe))
    {
        pRecordsRecipe = JSON.stringify(pRecordsRecipe);
    }
    var params = {
        "PresetRecipientsRecordsRecipe_param": pRecordsRecipe
    };
    var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters(params).toString();
    neon.openContextWithRecipe("BulkMail", "BulkMailEdit_view", recipe, neon.OPERATINGSTATE_NEW);
}

Johannes Hörmann's avatar
Johannes Hörmann committed
/**
 * 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(new CommunicationSettingsCondition()
                    .emails(CommUtil.getStandardSubSqlMail())
                    .rejected()
                    .existNoSettings()
                    .buildCondition())
/**
 * 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} pCondition Condition part of sys.filter
 * @param {String} pContext Context that belongs to the filtercondition
 * @return {String[]} contacts that can be added as recipients
 */
BulkMailUtils.filterNewRecipientsByCondition = function (pBulkMailId, pCondition, pContext)
{
    var condition = newSelect("CONTACTID")
                .from("CONTACT")
                .join("ADDRESS", "ADDRESS.ADDRESSID = CONTACT.ADDRESS_ID")
                .join("ORGANISATION", "ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID")
                .whereIfSet(pCondition)
                // 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(new CommunicationSettingsCondition()
                    .emails(CommUtil.getStandardSubSqlMail())
                    .rejected()
                    .existNoSettings()
                    .buildCondition());
    if (pContext == "Person")
    {
        condition.join("PERSON", "PERSON.PERSONID = CONTACT.PERSON_ID");
    {
        condition.and("CONTACT.PERSON_ID is null");
    }
    
    if (pContext == "CampaignParticipant")
    {
       condition.join("CAMPAIGNPARTICIPANT","CAMPAIGNPARTICIPANT.CONTACT_ID = CONTACT.CONTACTID");
    }
        
    if (pContext == "CampaignStep")
    {
       condition.join("CAMPAIGNPARTICIPANT","CAMPAIGNPARTICIPANT.CONTACT_ID = CONTACT.CONTACTID")
       .join("CAMPAIGNSTEP","CAMPAIGNSTEP.CAMPAIGNSTEPID = CAMPAIGNPARTICIPANT.CAMPAIGNSTEP_ID");
    }
    return  condition.arrayColumn();
}
Martin Groppe's avatar
Martin Groppe committed
/*
 * adds a Recipient to an ad hoc mailing list and sends the mail.
 *
 * @param {String} pBulkMailId id of the bulk mail the contact should be added to
 * @param {String} pContactId id of the contact that gets added
 * @param {String} pEmailAddress address the ad hoc mailing gets sent to.
 * @param {Object} pAdditionalLinkParameters(optional)<p>
 *                                              Additional parameters that get put into the weblinks for the redirect webservice.
 *                                              Expects object  of key value pairs. <br>   
 * @param {String} pOriginUrl Base URL for link replacement.
 */

BulkMailUtils.addToAdHocMail = function (pBulkMailId, pContactId, pEmailAddress, pAdditionalLinkParameters, pOriginUrl)
{
    if(!pBulkMailId || !pContactId)
    {
        return;
    }
   
    var bulkMailRecipientId = util.getNewUUID(); 
    new SqlBuilder().insertFields({
        "BULKMAILRECIPIENTID": bulkMailRecipientId,
        "BULKMAIL_ID": pBulkMailId,
        "CONTACT_ID": pContactId,
        "STATUS": $KeywordRegistry.bulkMailRecipientStatus$pending(),
        "EMAIL_ADDRESS": pEmailAddress
    }, 
    "BULKMAILRECIPIENT");
    
    this.sendBulkMail(pBulkMailId, false, false, pAdditionalLinkParameters, bulkMailRecipientId, pOriginUrl); 
}
/**
 * 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} pRecordsRecipe recordsrecipe for the selection that should be filtered.
 * @return {String[]} contacts that can be added as recipients
 */
BulkMailUtils.filterNewRecipientsByRecordsRecipe = function (pBulkMailId, pRecordsRecipe)
{
    var recipients = [];
    
    var entity = JSON.parse(pRecordsRecipe).entityName;
    
    var loadConfig = entities.createConfigForLoadingRows()
                    .fromEntityRecordsRecipe(pRecordsRecipe)
                    .fields(["#UID"]);
    var rows = entities.getRows(loadConfig);

    while (rows.length > 0)
    {
        var currentIds = rows.splice(0,1000).map(function (row)
        {
            return row["#UID"];
        });
        var sql = newSelect("CONTACTID")
                    .from("CONTACT")
                    .join("ADDRESS", "ADDRESS.ADDRESSID = CONTACT.ADDRESS_ID")
                    .join("ORGANISATION", "ORGANISATION.ORGANISATIONID = CONTACT.ORGANISATION_ID")
                    // only add contacts that aren't already recipients
                    .where(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(new CommunicationSettingsCondition()
                        .emails(CommUtil.getStandardSubSqlMail())
                        .rejected()
                        .existNoSettings()
                        .buildCondition());
        if (entity == "Person_entity")
        {
            sql.join("PERSON", "PERSON.PERSONID = CONTACT.PERSON_ID")
            .and("CONTACT.CONTACTID",currentIds,SqlBuilder.IN());
        }
        if (entity == "Organisation_entity")
        {
            sql.and("CONTACT.PERSON_ID is null")
            .and("CONTACT.CONTACTID",currentIds,SqlBuilder.IN());
        }

        if (entity == "CampaignParticipant_entity")
        {
           sql.join("CAMPAIGNPARTICIPANT","CAMPAIGNPARTICIPANT.CONTACT_ID = CONTACT.CONTACTID")
           .and("CAMPAIGNPARTICIPANT.CAMPAIGNPARTICIPANTID",currentIds,SqlBuilder.IN());
        }

        if (entity == "CampaignStep_entity")
        {
           sql.join("CAMPAIGNPARTICIPANT","CAMPAIGNPARTICIPANT.CONTACT_ID = CONTACT.CONTACTID")
           .join("CAMPAIGNSTEP","CAMPAIGNSTEP.CAMPAIGNSTEPID = CAMPAIGNPARTICIPANT.CAMPAIGNSTEP_ID")
           .and("CAMPAIGNSTEP.CAMPAIGNSTEPID",currentIds,SqlBuilder.IN());
        }
        recipients = recipients.concat(sql.arrayColumn());
    }

    return  recipients;
}

Johannes Hörmann's avatar
Johannes Hörmann committed
/**
 * Opens the given bulk mail.
 * 
 * @param {String} pBulkMailId          <p>
 *                                      The id of the bulk mail.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
BulkMailUtils.openBulkMail = function (pBulkMailId)
{
Pascal Neub's avatar
Pascal Neub committed
    var recipe = neonFilter.createEntityRecordsRecipeBuilder().uidsIncludelist([pBulkMailId]).toString();
    neon.openContextWithRecipe("BulkMail", "BulkMailMain_view", recipe, neon.OPERATINGSTATE_VIEW);
 * Checks is the given mime type can be used for a bulk mail.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 * 
 * @param {String} pMimeType                <p>
 *                                          The mime type.<br>
 * @return {Boolean}                        <p>
 *                                          Whether the type is usable or not.<br>
Johannes Hörmann's avatar
Johannes Hörmann committed
 */
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.<br>
 * @param {String} pTemplateType            <p>
 *                                          Template type.<br>
 * @return {Boolean}                        <p>
 *                                          Whether the type is usable or not.<br>
 */
BulkMailUtils.isValidTemplateType = function (pTemplateType)
{
    switch (pTemplateType)
Johannes Hörmann's avatar
Johannes Hörmann committed
    {
        case DocumentTemplate.types.EML:
        case DocumentTemplate.types.HTML:
        case DocumentTemplate.types.TXT:
            return true;
        default:
            return false;
    }
}

 * Checks whether the given status id matches,<br>
 * to the status of a bulk mail which is sent or<br>
 * not.
 *
 * @param {String} pStatus              <p>
 *                                      The key id of the current status.<br>
 * @return {Boolean}                    <p>
 *                                      True if the status is "sent","failed" or "sending".<br> 
 */
BulkMailUtils.isStatusSendingOrSent = function (pStatus)
{
    return pStatus == $KeywordRegistry.bulkMailStatus$sent() || pStatus == $KeywordRegistry.bulkMailStatus$beingSent() || pStatus == $KeywordRegistry.bulkMailStatus$failed();
Martin Groppe's avatar
Martin Groppe committed
/**
 * Checks whether the given status id matches,<br>
 * to the status of a bulk mail which is sent or<br>
 * not.
 *
 * @param {String} pStatus              <p>
 *                                      The key id of the current status.<br>
 * @return {Boolean}                    <p>
 *                                      True if the status is "sent" or "sending".<br> 
 */
BulkMailUtils.isStatusSendingSentOrAdHoc = function (pStatus)
{
    return pStatus == $KeywordRegistry.bulkMailStatus$sent() || pStatus == $KeywordRegistry.bulkMailStatus$beingSent() || pStatus == $KeywordRegistry.bulkMailStatus$adHoc()
}

/**
 * Opens BulkMail context in new mode, with the given bulk mail id.<br>
 * 
 * @param {String} pBulkMailId          <p>
 *                                      The id of the bulk mail.<br>
 */
Johannes Hörmann's avatar
Johannes Hörmann committed
BulkMailUtils.copy = function(pBulkMailId)
{
    var params = {
        "CopyBulkMailId_param" : pBulkMailId
    };
Pascal Neub's avatar
Pascal Neub committed
    var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters(params).toString();
    neon.openContextWithRecipe("BulkMail", null, recipe, neon.OPERATINGSTATE_NEW);

/**
 * Opens BulkMail context in new mode, with the given template from MosaicoTemplate id.<br>
 * 
 * @param {String} pMosaicoTemplateId          <p>
 *                                      The id of the bulk mail.<br>
 */
BulkMailUtils.createFromMosaicoTemplate = function(pMosaicoTemplateId)
{
    var params = {
        "CreateFromMosaicoTemplateId_param" : pMosaicoTemplateId
    };
Pascal Neub's avatar
Pascal Neub committed
    var recipe = neonFilter.createEntityRecordsRecipeBuilder().parameters(params).toString();
    neon.openContextWithRecipe("BulkMail", null, recipe, neon.OPERATINGSTATE_NEW);
/*
 *Stores the Eml file for a bulkmailrecipient in the Filesystem
 *
 * @param {String} pBulkMailId          <p>
 *                                      The id of the bulk mail.<br>
 * @param {String} pMailRunId          <p>
 *                                      The id of the bulk mail run.<br>
 * @param {String} pMailLogId          <p>
 *                                      The id of the corresponding mail log entry.<br>  
 * @param {String} pFile                                                            
BulkMailUtils.storeEmlFile = function (pBulkMailId, pMailRunId, pMailLogId, pFile)
    var locationOption = project.getInstanceConfigValue("bulkmail.fileStorage", vars.get("$sys.serverdata"));
    var path = locationOption + (locationOption.substr(locationOption.length-1) == "/" ? "" : "/" )+ "bulkmailfiles/" + pBulkMailId + "/" + pMailRunId + "/";
    var filename = pMailLogId + ".eml"
    var fullPath = path + filename;
    fileIO.storeData(fullPath, pFile, util.DATA_BINARY, false);
}