Skip to content
Snippets Groups Projects
process.js 13.84 KiB
import("system.question");
import("Sql_lib");
import("MimeType_lib");
import("Document_lib");
import("system.logging");
import("system.translate");
import("system.text");
import("system.db");
import("system.util");
import("Communication_lib");
import("DocumentTemplate_lib");
import("system.neon");
import("system.mail");
import("Bulkmail_lib");
import("system.tools");
import("EmailUtil_lib");

function EmailWritingUtils () {}

/**
 * creates a new E-Mail-Object and ask for a download of a eml where all fields are prefilled
 * The eml can be open with a mailclient and sent via the mailclient. Each mailclient has a different behaviour:
 * In Outlook the mail is automatically opened in draft-mode
 * In Thunderbird the mail is opened in view mode and you've to manually "edit as new"
 * 
 * @param {String|Array} pToRecipients mailaddresses of the recipients, can either be a 1D-Array with several addresses or a string with one address
 * @param {String} pSenderContactId contactId of the sender. the standard mailadress of the contact is used as sender-address
 * @param {String} [pTemplateId] if a document-template shall be used, give the templateId here
 * @param {String} [pRecipientContactId] contactId of the recipient, required to fill placeholders
 * @param {String} [pBindata] base64 binary data
 * @param {Array} [pAttachments] attachments in a array (base64 encoded).
 * @param {String} pSubject an optional subject.
 * @param {String} [pEmailFilename] filename of the email.
 * @param {Placeholder[]} [pAdditionalPlaceholders] additional placeholders
 * @return {Array} the eml document as array with [filename, base64]
 */
EmailWritingUtils.openMailTemplate = function (pToRecipients, pSenderContactId, pTemplateId, pRecipientContactId, pBindata, pAttachments, pSubject, pEmailFilename, pAdditionalPlaceholders)
{
    if (pToRecipients && typeof(pToRecipients) == "string")
        pToRecipients = [pToRecipients];
    
    var email;
    
    if (pTemplateId || (pBindata.bindata != "" && pBindata.bindata != null))
    {
        email = Email.fromTemplate(pTemplateId, pRecipientContactId, pBindata, pAdditionalPlaceholders);
        if (!email)
            email = new Email();
    }    
    else email = new Email();
    
    email.setSender(pSenderContactId);
    
    if (pToRecipients) 
        email.toRecipients = pToRecipients;
    
    email.bccRecipients = [EmailWritingUtils.getMailbridgeAddress()];
    
    if (pAttachments)
    {
        for (var i = 0; i < pAttachments.length; i++) 
        {
            if (pAttachments[i] != null || pAttachments[i] != "") 
                email.attachmentTemplates[i] = pAttachments[i];
        }       
    }
    
    if (pSubject)
        email.subject = pSubject;
    
    return email.downloadEML(pEmailFilename);
}



/**
 * opens a view where a new mail can be sent. In the view the use CAN select a DocumentTemplate if needed
 * 
 * @param {String} pToContactId contactId with contacts to filter the communication-addresses
 * @param {String} [pToEmailAddress] email address as string that shall be used as recipient-preset
 * @param {Array} pAttachmentArray array with attachments.
 * @param {String} pNotificationMsg message which will be shown after the operation is done.
 * @param {String} pComingFrom source from where you started (e.g. "Person", "Organisation" )
 * @param {String} pEmailFilename optional file name of the email.
 * @param {String} [pAdditionalPlaceholders] additional placeholders for the email
 * @param {Array[]} [pUpdateStatements] an array with update statements which will be executed after email is downloaded/sent.
 * @param {String} pTypeClassification Documenttemplatetypeclassification
 */
EmailWritingUtils.openNewMail = function (pToContactId, pToEmailAddress, pComingFrom, pAttachmentArray, pNotificationMsg, pEmailFilename, pAdditionalPlaceholders, pUpdateStatements, pTypeClassification)
{
    var params = {
        "ContactId_param" : pToContactId,
        "Attachments_param" : JSON.stringify(pAttachmentArray),
        "ComingFrom_param" : pComingFrom,
        "NotificationMsg_param" : pNotificationMsg,
        "EmailFilename" : pEmailFilename,
        "AdditionalPlaceholders_param" : JSON.stringify(pAdditionalPlaceholders),
        "UpdateStatements_param" : JSON.stringify(pUpdateStatements),
        "DocumentTemplateTypeClassification_param" : pTypeClassification
    };
    
    if (pToEmailAddress)
        params.Recipient_param = pToEmailAddress;
    
    neon.openContext("Email", "EmailEdit_view", null, neon.OPERATINGSTATE_VIEW, params);
}


EmailWritingUtils.getMailbridgeAddress = function ()
{
    return "mailbridge@domain.local"; //TODO: not hardcoded
}

/**
 * Opens a view where the email with the report(s) can be downloaded as .eml
 * and after that it can be sent with e.g. Thunderbird
 * 
 * @param {String} pRecipient Contact id of the recipient. 
 * @param {String} pComingFrom source from where you started (e.g. "Person", "Organisation")
 * @param {Array} pReportArray array with reports.
 * @param {String} pNotificationMsg message which will be shown after the operation is done.
 * @param {String} pEmailFilename optional file name of the email.
 * @param {String} [pAdditionalPlaceholders] additional placeholders for the email
 * @param {String} [pOfferId] optional needed for the offerEmails, to change the status of the offer
 * Report have to be a object with these attrs: content (base64 encoded report), contentType (mimeType of the report), filename (complete filename with filending)
 */
EmailWritingUtils.sendReportAsMail = function (pRecipient, pReportArray, pComingFrom, pNotificationMsg, pEmailFilename, pAdditionalPlaceholders, pOfferId)
{
    var pRecpientEmail;
    
    if (pRecipient)
    {
        pRecpientEmail = newSelect("COMMUNICATION.ADDR").from("COMMUNICATION")
                                .where("COMMUNICATION.CONTACT_ID", pRecipient).and("COMMUNICATION.MEDIUM_ID", "COMMEMAIL").cell();        
    } else{
        pRecpientEmail = null;
    }
    
    EmailWritingUtils.openNewMail(pRecipient, pRecpientEmail, pComingFrom, pReportArray, pNotificationMsg, pEmailFilename, pAdditionalPlaceholders, pOfferId);
}

/**
 * object for handling emails
 * @param {String} [pBody=null] if given, the body is set to this text
 * @class
 */
function Email(pBody)
{
    if (pBody == undefined) pBody = null;
    
    this.sender = null;
    this.subject = null;
    this.body = pBody;
    this.mailText = null;
    this.toRecipients = [];
    this.ccRecipients = [];
    this.bccRecipients = [];
    this.attachmentTemplates = [];
    this.emlFile = null;
}

/**
 * makes an Email object from a RFC
 * NOTE: body is not editable if comming from a eml. Use it only for preview!
 * 
 * @param {String} pBase64RFC the RFC mail, base64 encoded
 * @return {Email} a new Email object
 */
Email.fromRFC = function (pBase64RFC)
{
    var decoded = util.decodeBase64String(pBase64RFC);
    var mailData = mail.parseRFC(decoded);
    var newMail = new Email(mailData[mail.MAIL_HTMLTEXT]);
    newMail.sender = mailData[mail.MAIL_SENDER];
    newMail.mailText = mailData[mail.MAIL_TEXT];
    newMail.subject = mailData[mail.MAIL_SUBJECT];
    newMail.toRecipients = mailData[mail.RECIPIENT_TO];
    newMail.ccRecipients = mailData[mail.RECIPIENT_CC];
    newMail.bccRecipients = mailData[mail.RECIPIENT_BCC];
    newMail.emlFile = decoded;
    return newMail;
}

/**
 * Returns a object containing the contact ids as
 * keys and the corresponding Email objects as values.
 * 
 * @param {String} [pTemplateId] UUID of the explicit template which shall used. (optional)
 * @param {String} [pContactId] (required)
 * @param {String} [pBindata] (required)
 * @param {Placeholder[]} [pAdditionalPlaceholders] additional placeholders
 * @return {Email} a new Email object
 */
Email.fromTemplate = function (pTemplateId, pContactId, pBindata, pAdditionalPlaceholders)
{
    var template;
    
    if (pBindata && pBindata.isFilled() && BulkMailUtils.isValidMimeType(pBindata.mimeType))
    {
        template = new DocumentTemplate(pBindata.bindata, DocumentTemplate.types.fromMimeType(pBindata.mimeType), pBindata.filename, true);
    }
    else
    {
        template = DocumentTemplate.loadTemplate(pTemplateId);
        
        if (!template)
            return null;
    }
    
    return template.getReplacedEmailsByContactIds([pContactId], pAdditionalPlaceholders)[pContactId];
}

/**
 * Replaces the placeholders with data from the contacts and returns the resulting Emails.
 * @param {DocumentTemplate} pTemplate a document template which is used for all mails
 * @param {Array} pContactIds Contact ids of all recipients
 * 
 * @return {Object} Object containing the contact ids as keys and the corresponding Email
 *                   objects as values 
 */
Email.getReplacedBulkEmails = function(pTemplate, pContactIds) 
{
    return pTemplate.getReplacedEmailsByContactIds(pContactIds);
}

/**
 * sets the sender of the mail
 * 
 * @param {String} pContactId the contactId of the sender
 */
Email.prototype.setSender = function (pContactId)
{
    this.sender = CommUtil.getStandardMail(pContactId);
}

/**
 * generates a 'mailto:' URL from the email object
 */
Email.prototype.getMailtoUrl = function ()
{
    var url = [];
    
    if (this.toRecipients.length)
        url.push("to=" + this.toRecipients.join());
    
    if (this.ccRecipients.length)
        url.push("cc=" + this.ccRecipients.join());
    
    if (this.bccRecipients.length)
        url.push("bcc=" + this.bccRecipients.join());
    
    if (this.subject)
        url.push("subject=" + this.subject);
    
    if (this.body)
        url.push("body=" + text.html2text(this.body));
    
    url = "mailto:?" + url.join("&");
    
    return encodeURI(url);
}

Email.prototype._newMailObject = function()
{
    var ENCODING = "UTF-8";
    var mailId;
    var emlFile = this.emlFile ? this.emlFile : null;
        
    if (emlFile)
        mailId = mail.newMail(emlFile, mail.FORMAT_MIME);
    else
        mailId = mail.newMail();
    
    if (emlFile)
    {
        mail.clearRecipients(mailId, mail.RECIPIENT_TO);
        mail.clearRecipients(mailId, mail.RECIPIENT_CC);
        mail.clearRecipients(mailId, mail.RECIPIENT_BCC);
    }
    
    if (this.sender)
        mail.setSender(mailId, this.sender);
        
    if (this.toRecipients.length)
        mail.addRecipients(mailId, mail.RECIPIENT_TO, this.toRecipients);

    if (this.ccRecipients.length)
        mail.addRecipients(mailId, mail.RECIPIENT_CC, this.ccRecipients);

    if (this.bccRecipients.length)
        mail.addRecipients(mailId, mail.RECIPIENT_BCC, this.bccRecipients);
        
    mail.setSubject(mailId, this.subject || "", ENCODING);
        
    // only alter subject and text, if no eml is used
    if (!emlFile)
    {
        if (this.body)
            mail.addText(mailId, this.body, "text/html", ENCODING, null);
        else
            mail.addText(mailId, "", "text/html", ENCODING, null);
    }
    
    this.attachmentTemplates.forEach(function(pAttachment)
    {
        if (pAttachment.templateId) 
        {
            mail.addBase64Attachment(mailId, pAttachment.content, 
                DocumentTemplateUtils.getMimeType(pAttachment.templateId), pAttachment.filename, true, null);
        }
        else
            mail.addBase64Attachment(mailId, pAttachment.content, pAttachment.mimeType, pAttachment.filename, true, null, null)
    });
    
    return mailId;
}

/**
 * generates a eml-element from the email object
 */
Email.prototype.getRFCmail = function ()
{
    var mailId = this._newMailObject()
    
    //"X-Unsent" is a very badly, non-standardised header to gently ask the mail client that the mail should open in a compose-mode
    //this is mainly done for Microsoft Outlook for Windows. 
    //Thunderbird has a dinosaur-request (it's from the year 2002) to also support this: https://bugzilla.mozilla.org/show_bug.cgi?id=166541 
    mail.addHeader(mailId, "X-Unsent", "1");
    
    //accoding to this entry: https://stackoverflow.com/questions/11330628/os-x-mail-open-eml-files-in-compose-mode/33224913
    //something similar exists for OS X Mail
    //X-Uniform-Type-Identifier: com.apple.mail-draft
    //this could be added later if needed

    var mailObj = mail.getCachedMail(mailId);
    var finalMail = mail.toRFC(mailObj);
    
    // remove from cache
    mail.deleteMail(mailId)
    return finalMail;
}

/**
 * opens the email
 */
Email.prototype.openMail = function ()
{
    neon.openUrl(this.getMailtoUrl(), false);
}
/**
 * ask for a download of the email
 * 
 * @param {String} [pFilename] the file name.
 * @return {Array} array of [filename, EML (base64)]
 */
Email.prototype.downloadEML = function (pFilename)
{
    var eml = this.getEML();
    var filename = (pFilename || translate.text("Email Template")) + ".eml";
    neon.download(eml, filename);
    return [filename, eml];
}

/**
 * @return {String} RFC mail (base64 encoded)
 */
Email.prototype.getEML = function ()
{ 
    return util.encodeBase64String(this.getRFCmail(), null);
}

/**
 * sends the email object
 * 
 * @return {boolean} true, if the mail was sent sucessfully
 */
Email.prototype.send = function (pUser)
{
    try
    {
        mailId = this._newMailObject();
        
        if (this.sender)
            mail.setSender(mailId, this.sender);
        var mailbridgeTitle = "mailbridge";
        var sentMails;
        if(pUser)
            sentMails = mail.sendMailAs(pUser, mailId)
        else
        {
            let mailbridgeExists = tools.getUser(mailbridgeTitle);
            
            if(mailbridgeExists)
                mail.sendMailAs(mailbridgeTitle, mailId)
            else //check mailbridge user if this error gets thrown
                question.showMessage(translate.withArguments("Mailbridge failed: user '%0' is unknown, contact an administrator.", [mailbridgeTitle]), question.ERROR, translate.text("Error"));
        }
        // remove from cache
        mail.deleteMail(mailId)
        return sentMails > 0;
    }
    catch (ex)
    {
        logging.log(ex);
        return false;
    }
}