return CRM.resourceUrls['civicrm'] + '/partials/crmMailing2/' + relPath;
};
+ // FIXME: surely there's already some helper which can do this in one line?
+ // @return string "YYYY-MM-DD hh:mm:ss"
+ var createNow = function () {
+ var currentdate = new Date();
+ var yyyy = currentdate.getFullYear();
+ var mm = currentdate.getMonth() + 1;
+ mm = mm < 10 ? '0' + mm : mm;
+ var dd = currentdate.getDate();
+ dd = dd < 10 ? '0' + dd : dd;
+ var hh = currentdate.getHours();
+ hh = hh < 10 ? '0' + hh : hh;
+ var min = currentdate.getMinutes();
+ min = min < 10 ? '0' + min : min;
+ var sec = currentdate.getSeconds();
+ sec = sec < 10 ? '0' + sec : sec;
+ return yyyy + "-" + mm + "-" + dd + " " + hh + ":" + min + ":" + sec;
+ };
+
+ // FIXME: Load status ids from DB
+ var APPROVAL_STATUSES = { Approved: "1", Rejected: "2", None: "3" };
+
var crmMailing2 = angular.module('crmMailing2');
- crmMailing2.factory('crmMailingMgr', function($q, crmApi) {
+ // The representation of from/reply-to addresses is inconsistent in the mailing data-model,
+ // so the UI must do some adaptation. The crmFromAddresses provides a richer way to slice/dice
+ // the available "From:" addrs. Records are like the underlying OptionValues -- but add "email"
+ // and "author".
+ crmMailing2.factory('crmFromAddresses', function($q, crmApi) {
+ var emailRegex = /^"(.*)" \<([^@\>]*@[^@\>]*)\>$/;
+ var addrs = _.map(CRM.crmMailing.fromAddress, function(addr){
+ var match = emailRegex.exec(addr.label);
+ return _.extend({}, addr, {
+ email: match ? match[2] : '(INVALID)',
+ author: match ? match[1] : '(INVALID)'
+ });
+ });
+ function first(array) {
+ return (array.length == 0) ? null : array[0];
+ }
+
+ return {
+ getAll: function getAll() {
+ return addrs;
+ },
+ getByAuthorEmail: function getByAuthorEmail(author, email, autocreate) {
+ var result = null;
+ _.each(addrs, function(addr){
+ if (addr.author == author && addr.email == email) {
+ result = addr;
+ }
+ });
+ if (!result && autocreate) {
+ result = {
+ label: '(INVALID) "' + author + '" <' + email + '>',
+ author: author,
+ email: email
+ };
+ addrs.push(result);
+ }
+ return result;
+ },
+ getByEmail: function getByEmail(email) {
+ return first(_.where(addrs, {email: email}));
+ },
+ getByLabel: function(label) {
+ return first(_.where(addrs, {label: label}));
+ },
+ getDefault: function getDefault() {
+ return first(_.where(addrs, {is_default: "1"}));
+ }
+ };
+ });
+
+ crmMailing2.factory('crmMsgTemplates', function($q, crmApi) {
+ var tpls = _.map(CRM.crmMailing.mesTemplate, function(tpl){
+ return _.extend({}, tpl, {
+ //id: tpl parseInt(tpl.id)
+ });
+ });
+ window.tpls = tpls;
+ var lastModifiedTpl = null;
+ return {
+ // @return Promise MessageTemplate (per APIv3)
+ get: function get(id) {
+ id = ''+id; // parseInt(id);
+ var dfr = $q.defer();
+ var tpl = _.where(tpls, {id: id});
+ if (id && tpl && tpl[0]) {
+ dfr.resolve(tpl[0]);
+ } else {
+ dfr.reject(id);
+ }
+ return dfr.promise;
+ },
+ // Save a template
+ // @param tpl MessageTemplate (per APIv3) For new templates, omit "id"
+ // @return Promise MessageTemplate (per APIv3)
+ save: function(tpl) {
+ return crmApi('MessageTemplate', 'create', tpl).then(function(response){
+ if (!tpl.id) {
+ tpl.id = ''+response.id; //parseInt(response.id);
+ tpls.push(tpl);
+ }
+ lastModifiedTpl = tpl
+ return tpl;
+ });
+ },
+ // @return Object MessageTemplate (per APIv3)
+ getLastModifiedTpl: function() {
+ return lastModifiedTpl;
+ },
+ getAll: function getAll() {
+ return tpls;
+ }
+ };
+ });
+
+ // The crmMailingMgr service provides business logic for loading, saving, previewing, etc
+ crmMailing2.factory('crmMailingMgr', function($q, crmApi, crmFromAddresses) {
var pickDefaultMailComponent = function pickDefaultMailComponent(type) {
var mcs = _.where(CRM.crmMailing.headerfooterList, {
component_type:type,
mailing.mailings = {include: [], exclude: []};
_.each(groupResult.values, function(mailingGroup) {
var bucket = (mailingGroup.entity_table == 'civicrm_group') ? 'groups' : 'mailings';
- mailing[bucket][mailingGroup.group_type].push(mailingGroup.entity_id);
+ var entityId = parseInt(mailingGroup.entity_id);
+ mailing[bucket][mailingGroup.group_type].push(entityId);
});
return mailing;
});
return {
name: "revert this", // fixme
campaign_id: null,
- from: _.where(CRM.crmMailing.fromAddress, {is_default: "1"})[0].label,
+ from_name: crmFromAddresses.getDefault().author,
+ from_email: crmFromAddresses.getDefault().email,
replyto_email: "",
subject: "For {contact.display_name}", // fixme
dedupe_email: "1",
};
},
+ // @param mailing Object (per APIv3)
+ // @return Promise
+ 'delete': function(mailing) {
+ if (mailing.id) {
+ return crmApi('Mailing', 'delete', {id: mailing.id});
+ } else {
+ var d = $q.defer();
+ d.resolve();
+ return d.promise;
+ }
+ },
+
+ // Copy all data fields in (mailingFrom) to (mailingTgt) -- except for (excludes)
+ // ex: crmMailingMgr.mergeInto(newMailing, mailingTemplate, ['subject']);
+ mergeInto: function mergeInto(mailingTgt, mailingFrom, excludes) {
+ var MAILING_FIELDS = [
+ // always exclude: 'id'
+ 'name',
+ 'campaign_id',
+ 'from_name',
+ 'from_email',
+ 'replyto_email',
+ 'subject',
+ 'dedupe_email',
+ 'groups',
+ 'mailings',
+ 'body_html',
+ 'body_text',
+ 'footer_id',
+ 'header_id',
+ 'visibility',
+ 'url_tracking',
+ 'dedupe_email',
+ 'forward_replies',
+ 'auto_responder',
+ 'open_tracking',
+ 'override_verp',
+ 'optout_id',
+ 'reply_id',
+ 'resubscribe_id',
+ 'unsubscribe_id'
+ ];
+ if (!excludes) {
+ excludes = [];
+ }
+ _.each(MAILING_FIELDS, function (field) {
+ if (!_.contains(excludes, field)) {
+ mailingTgt[field] = mailingFrom[field];
+ }
+ })
+ },
+
// @param mailing Object (per APIv3)
// @return Promise an object with "subject", "body_text", "body_html"
preview: function preview(mailing) {
var params = _.extend({}, mailing, {
options: {force_rollback: 1},
'api.Mailing.preview': {
- id: '$value.id',
+ id: '$value.id'
}
});
return crmApi('Mailing', 'create', params).then(function(result){
// get the resulting recipients -- then rollback any changes.
var params = _.extend({}, mailing, {
options: {force_rollback: 1},
+ 'api.mailing_job.create': 1, // note: exact match to API default
'api.MailingRecipients.get': {
mailing_id: '$value.id',
options: { limit: previewLimit },
});
},
+ // Save a (draft) mailing
+ // @param mailing Object (per APIv3)
+ // @return Promise
+ save: function(mailing) {
+ var params = _.extend({}, mailing, {
+ 'api.mailing_job.create': 0 // note: exact match to API default
+ });
+
+ // WORKAROUND: Mailing.create (aka CRM_Mailing_BAO_Mailing::create()) interprets scheduled_date
+ // as an *intent* to schedule and creates tertiary records. Saving a draft with a scheduled_date
+ // is therefore not allowed. Remove this after fixing Mailing.create's contract.
+ delete params.scheduled_date;
+
+ return crmApi('Mailing', 'create', params).then(function(result){
+ if (result.id && !mailing.id) mailing.id = result.id; // no rollback, so update mailing.id
+ // Perhaps we should reload mailing based on result?
+ return result.values[result.id];
+ });
+ },
+
+ // Schedule/send the mailing
+ // @param mailing Object (per APIv3)
+ // @return Promise
+ submit: function (mailing) {
+ var changes = {
+ approval_date: createNow(),
+ approver_id: CRM.crmMailing.contactid,
+ approval_status_id: APPROVAL_STATUSES.Approved,
+ scheduled_date: mailing.scheduled_date ? mailing.scheduled_date : createNow(),
+ scheduled_id: CRM.crmMailing.contactid
+ };
+ var params = _.extend({}, mailing, changes, {
+ 'api.mailing_job.create': 0 // note: exact match to API default
+ });
+ return crmApi('Mailing', 'create', params).then(function (result) {
+ if (result.id && !mailing.id) mailing.id = result.id; // no rollback, so update mailing.id
+ _.extend(mailing, changes); // Perhaps we should reload mailing based on result?
+ return result.values[result.id];
+ });
+ },
+
+ // Immediately send a test message
// @param mailing Object (per APIv3)
// @param testEmail string
// @param testGroup int (id#)
// @return Promise for a list of delivery reports
sendTest: function(mailing, testEmail, testGroup) {
var params = _.extend({}, mailing, {
- // options: {force_rollback: 1},
+ // options: {force_rollback: 1}, // Test mailings include tracking features, so the mailing must be persistent
'api.Mailing.send_test': {
mailing_id: '$value.id',
test_email: testEmail,
test_group: testGroup
}
});
+
+ // WORKAROUND: Mailing.create (aka CRM_Mailing_BAO_Mailing::create()) interprets scheduled_date
+ // as an *intent* to schedule and creates tertiary records. Saving a draft with a scheduled_date
+ // is therefore not allowed. Remove this after fixing Mailing.create's contract.
+ delete params.scheduled_date;
+
return crmApi('Mailing', 'create', params).then(function(result){
if (result.id && !mailing.id) mailing.id = result.id; // no rollback, so update mailing.id
return result.values[result.id]['api.Mailing.send_test'].values;
}
};
});
-
})(angular, CRM.$, CRM._);