Commit | Line | Data |
---|---|---|
8dfd5110 TO |
1 | (function (angular, $, _) { |
2 | var partialUrl = function (relPath) { | |
3 | return CRM.resourceUrls['civicrm'] + '/partials/crmMailing2/' + relPath; | |
4 | }; | |
5 | ||
43102e47 TO |
6 | // FIXME: surely there's already some helper which can do this in one line? |
7 | // @return string "YYYY-MM-DD hh:mm:ss" | |
8 | var createNow = function () { | |
9 | var currentdate = new Date(); | |
10 | var yyyy = currentdate.getFullYear(); | |
11 | var mm = currentdate.getMonth() + 1; | |
12 | mm = mm < 10 ? '0' + mm : mm; | |
13 | var dd = currentdate.getDate(); | |
14 | dd = dd < 10 ? '0' + dd : dd; | |
15 | var hh = currentdate.getHours(); | |
16 | hh = hh < 10 ? '0' + hh : hh; | |
17 | var min = currentdate.getMinutes(); | |
18 | min = min < 10 ? '0' + min : min; | |
19 | var sec = currentdate.getSeconds(); | |
20 | sec = sec < 10 ? '0' + sec : sec; | |
21 | return yyyy + "-" + mm + "-" + dd + " " + hh + ":" + min + ":" + sec; | |
22 | }; | |
23 | ||
24 | // FIXME: Load status ids from DB | |
25 | var APPROVAL_STATUSES = { Approved: "1", Rejected: "2", None: "3" }; | |
26 | ||
8dfd5110 TO |
27 | var crmMailing2 = angular.module('crmMailing2'); |
28 | ||
a0214785 TO |
29 | // The representation of from/reply-to addresses is inconsistent in the mailing data-model, |
30 | // so the UI must do some adaptation. The crmFromAddresses provides a richer way to slice/dice | |
31 | // the available "From:" addrs. Records are like the underlying OptionValues -- but add "email" | |
32 | // and "author". | |
33 | crmMailing2.factory('crmFromAddresses', function($q, crmApi) { | |
34 | var emailRegex = /^"(.*)" \<([^@\>]*@[^@\>]*)\>$/; | |
35 | var addrs = _.map(CRM.crmMailing.fromAddress, function(addr){ | |
36 | var match = emailRegex.exec(addr.label); | |
37 | return _.extend({}, addr, { | |
38 | email: match ? match[2] : '(INVALID)', | |
39 | author: match ? match[1] : '(INVALID)' | |
40 | }); | |
41 | }); | |
42 | function first(array) { | |
43 | return (array.length == 0) ? null : array[0]; | |
52f515c6 | 44 | } |
a0214785 TO |
45 | |
46 | return { | |
47 | getAll: function getAll() { | |
48 | return addrs; | |
49 | }, | |
50 | getByAuthorEmail: function getByAuthorEmail(author, email, autocreate) { | |
51 | var result = null; | |
52 | _.each(addrs, function(addr){ | |
53 | if (addr.author == author && addr.email == email) { | |
54 | result = addr; | |
55 | } | |
56 | }); | |
57 | if (!result && autocreate) { | |
58 | result = { | |
59 | label: '(INVALID) "' + author + '" <' + email + '>', | |
60 | author: author, | |
61 | email: email | |
62 | }; | |
63 | addrs.push(result); | |
64 | } | |
65 | return result; | |
66 | }, | |
67 | getByEmail: function getByEmail(email) { | |
68 | return first(_.where(addrs, {email: email})); | |
69 | }, | |
70 | getByLabel: function(label) { | |
71 | return first(_.where(addrs, {label: label})); | |
72 | }, | |
73 | getDefault: function getDefault() { | |
74 | return first(_.where(addrs, {is_default: "1"})); | |
75 | } | |
76 | }; | |
77 | }); | |
78 | ||
744bebee TO |
79 | crmMailing2.factory('crmMsgTemplates', function($q, crmApi) { |
80 | var tpls = _.map(CRM.crmMailing.mesTemplate, function(tpl){ | |
81 | return _.extend({}, tpl, { | |
82 | //id: tpl parseInt(tpl.id) | |
83 | }); | |
84 | }); | |
85 | window.tpls = tpls; | |
86 | var lastModifiedTpl = null; | |
87 | return { | |
88 | // @return Promise MessageTemplate (per APIv3) | |
89 | get: function get(id) { | |
90 | id = ''+id; // parseInt(id); | |
91 | var dfr = $q.defer(); | |
92 | var tpl = _.where(tpls, {id: id}); | |
93 | if (id && tpl && tpl[0]) { | |
94 | dfr.resolve(tpl[0]); | |
95 | } else { | |
96 | dfr.reject(id); | |
97 | } | |
98 | return dfr.promise; | |
99 | }, | |
100 | // Save a template | |
101 | // @param tpl MessageTemplate (per APIv3) For new templates, omit "id" | |
102 | // @return Promise MessageTemplate (per APIv3) | |
103 | save: function(tpl) { | |
104 | return crmApi('MessageTemplate', 'create', tpl).then(function(response){ | |
105 | if (!tpl.id) { | |
106 | tpl.id = ''+response.id; //parseInt(response.id); | |
107 | tpls.push(tpl); | |
108 | } | |
109 | lastModifiedTpl = tpl | |
110 | return tpl; | |
111 | }); | |
112 | }, | |
113 | // @return Object MessageTemplate (per APIv3) | |
114 | getLastModifiedTpl: function() { | |
115 | return lastModifiedTpl; | |
116 | }, | |
117 | getAll: function getAll() { | |
118 | return tpls; | |
119 | } | |
120 | }; | |
121 | }); | |
122 | ||
a0214785 TO |
123 | // The crmMailingMgr service provides business logic for loading, saving, previewing, etc |
124 | crmMailing2.factory('crmMailingMgr', function($q, crmApi, crmFromAddresses) { | |
4dd19229 | 125 | var pickDefaultMailComponent = function pickDefaultMailComponent(type) { |
8dfd5110 TO |
126 | var mcs = _.where(CRM.crmMailing.headerfooterList, { |
127 | component_type:type, | |
128 | is_default: "1" | |
129 | }); | |
130 | return (mcs.length >= 1) ? mcs[0].id : null; | |
131 | }; | |
132 | ||
133 | return { | |
134 | // @param scalar idExpr a number or the literal string 'new' | |
135 | // @return Promise|Object Mailing (per APIv3) | |
4dd19229 | 136 | getOrCreate: function getOrCreate(idExpr) { |
8dfd5110 TO |
137 | return (idExpr == 'new') ? this.create() : this.get(idExpr); |
138 | }, | |
139 | // @return Promise Mailing (per APIv3) | |
4dd19229 | 140 | get: function get(id) { |
8dfd5110 TO |
141 | return crmApi('Mailing', 'getsingle', {id: id}).then(function(mailing){ |
142 | return crmApi('MailingGroup', 'get', {mailing_id: id}).then(function(groupResult){ | |
143 | mailing.groups = {include: [], exclude: []}; | |
144 | mailing.mailings = {include: [], exclude: []}; | |
145 | _.each(groupResult.values, function(mailingGroup) { | |
146 | var bucket = (mailingGroup.entity_table == 'civicrm_group') ? 'groups' : 'mailings'; | |
89a50c67 TO |
147 | var entityId = parseInt(mailingGroup.entity_id); |
148 | mailing[bucket][mailingGroup.group_type].push(entityId); | |
8dfd5110 TO |
149 | }); |
150 | return mailing; | |
151 | }); | |
152 | }); | |
153 | }, | |
154 | // @return Object Mailing (per APIv3) | |
4dd19229 | 155 | create: function create() { |
8dfd5110 TO |
156 | return { |
157 | name: "revert this", // fixme | |
158 | campaign_id: null, | |
a0214785 TO |
159 | from_name: crmFromAddresses.getDefault().author, |
160 | from_email: crmFromAddresses.getDefault().email, | |
8dfd5110 TO |
161 | replyto_email: "", |
162 | subject: "For {contact.display_name}", // fixme | |
163 | dedupe_email: "1", | |
164 | groups: {include: [2], exclude: [4]}, // fixme | |
165 | mailings: {include: [], exclude: []}, | |
166 | body_html: "<b>Hello</b> {contact.display_name}", // fixme | |
167 | body_text: "Hello {contact.display_name}", // fixme | |
168 | footer_id: null, // pickDefaultMailComponent('Footer'), | |
169 | header_id: null, // pickDefaultMailComponent('Header'), | |
170 | visibility: "Public Pages", | |
171 | url_tracking: "1", | |
172 | dedupe_email: "1", | |
173 | forward_replies: "0", | |
174 | auto_responder: "0", | |
175 | open_tracking: "1", | |
176 | override_verp: "1", | |
177 | optout_id: pickDefaultMailComponent('OptOut'), | |
178 | reply_id: pickDefaultMailComponent('Reply'), | |
179 | resubscribe_id: pickDefaultMailComponent('Resubscribe'), | |
180 | unsubscribe_id: pickDefaultMailComponent('Unsubscribe') | |
181 | }; | |
182 | }, | |
183 | ||
705c61e9 TO |
184 | // @param mailing Object (per APIv3) |
185 | // @return Promise | |
186 | 'delete': function(mailing) { | |
187 | if (mailing.id) { | |
188 | return crmApi('Mailing', 'delete', {id: mailing.id}); | |
189 | } else { | |
190 | var d = $q.defer(); | |
191 | d.resolve(); | |
192 | return d.promise; | |
193 | } | |
194 | }, | |
195 | ||
07fa6426 TO |
196 | // Copy all data fields in (mailingFrom) to (mailingTgt) -- except for (excludes) |
197 | // ex: crmMailingMgr.mergeInto(newMailing, mailingTemplate, ['subject']); | |
198 | mergeInto: function mergeInto(mailingTgt, mailingFrom, excludes) { | |
199 | var MAILING_FIELDS = [ | |
70980d8e | 200 | // always exclude: 'id' |
07fa6426 TO |
201 | 'name', |
202 | 'campaign_id', | |
203 | 'from_name', | |
204 | 'from_email', | |
205 | 'replyto_email', | |
206 | 'subject', | |
207 | 'dedupe_email', | |
208 | 'groups', | |
209 | 'mailings', | |
210 | 'body_html', | |
211 | 'body_text', | |
212 | 'footer_id', | |
213 | 'header_id', | |
214 | 'visibility', | |
215 | 'url_tracking', | |
216 | 'dedupe_email', | |
217 | 'forward_replies', | |
218 | 'auto_responder', | |
219 | 'open_tracking', | |
220 | 'override_verp', | |
221 | 'optout_id', | |
222 | 'reply_id', | |
223 | 'resubscribe_id', | |
224 | 'unsubscribe_id' | |
225 | ]; | |
226 | if (!excludes) { | |
227 | excludes = []; | |
228 | } | |
229 | _.each(MAILING_FIELDS, function (field) { | |
230 | if (!_.contains(excludes, field)) { | |
231 | mailingTgt[field] = mailingFrom[field]; | |
232 | } | |
233 | }) | |
234 | }, | |
235 | ||
493eb47a TO |
236 | // @param mailing Object (per APIv3) |
237 | // @return Promise an object with "subject", "body_text", "body_html" | |
238 | preview: function preview(mailing) { | |
239 | var params = _.extend({}, mailing, { | |
240 | options: {force_rollback: 1}, | |
241 | 'api.Mailing.preview': { | |
52f515c6 | 242 | id: '$value.id' |
493eb47a TO |
243 | } |
244 | }); | |
245 | return crmApi('Mailing', 'create', params).then(function(result){ | |
beab9d1b | 246 | // changes rolled back, so we don't care about updating mailing |
493eb47a TO |
247 | return result.values[result.id]['api.Mailing.preview'].values; |
248 | }); | |
249 | }, | |
250 | ||
8dfd5110 TO |
251 | // @param mailing Object (per APIv3) |
252 | // @param int previewLimit | |
253 | // @return Promise for a list of recipients (mailing_id, contact_id, api.contact.getvalue, api.email.getvalue) | |
4dd19229 | 254 | previewRecipients: function previewRecipients(mailing, previewLimit) { |
8dfd5110 TO |
255 | // To get list of recipients, we tentatively save the mailing and |
256 | // get the resulting recipients -- then rollback any changes. | |
257 | var params = _.extend({}, mailing, { | |
258 | options: {force_rollback: 1}, | |
7575b840 | 259 | 'api.mailing_job.create': 1, // note: exact match to API default |
8dfd5110 TO |
260 | 'api.MailingRecipients.get': { |
261 | mailing_id: '$value.id', | |
262 | options: { limit: previewLimit }, | |
263 | 'api.contact.getvalue': {'return': 'display_name'}, | |
264 | 'api.email.getvalue': {'return': 'email'} | |
265 | } | |
266 | }); | |
267 | return crmApi('Mailing', 'create', params).then(function(recipResult){ | |
beab9d1b | 268 | // changes rolled back, so we don't care about updating mailing |
8dfd5110 TO |
269 | return recipResult.values[recipResult.id]['api.MailingRecipients.get'].values; |
270 | }); | |
beab9d1b TO |
271 | }, |
272 | ||
43102e47 | 273 | // Save a (draft) mailing |
705c61e9 TO |
274 | // @param mailing Object (per APIv3) |
275 | // @return Promise | |
276 | save: function(mailing) { | |
7575b840 TO |
277 | var params = _.extend({}, mailing, { |
278 | 'api.mailing_job.create': 0 // note: exact match to API default | |
279 | }); | |
43102e47 TO |
280 | |
281 | // WORKAROUND: Mailing.create (aka CRM_Mailing_BAO_Mailing::create()) interprets scheduled_date | |
282 | // as an *intent* to schedule and creates tertiary records. Saving a draft with a scheduled_date | |
283 | // is therefore not allowed. Remove this after fixing Mailing.create's contract. | |
284 | delete params.scheduled_date; | |
285 | ||
705c61e9 TO |
286 | return crmApi('Mailing', 'create', params).then(function(result){ |
287 | if (result.id && !mailing.id) mailing.id = result.id; // no rollback, so update mailing.id | |
43102e47 | 288 | // Perhaps we should reload mailing based on result? |
705c61e9 TO |
289 | return result.values[result.id]; |
290 | }); | |
291 | }, | |
292 | ||
293 | // Schedule/send the mailing | |
294 | // @param mailing Object (per APIv3) | |
295 | // @return Promise | |
43102e47 TO |
296 | submit: function (mailing) { |
297 | var changes = { | |
298 | approval_date: createNow(), | |
299 | approver_id: CRM.crmMailing.contactid, | |
300 | approval_status_id: APPROVAL_STATUSES.Approved, | |
301 | scheduled_date: mailing.scheduled_date ? mailing.scheduled_date : createNow(), | |
302 | scheduled_id: CRM.crmMailing.contactid | |
303 | }; | |
304 | var params = _.extend({}, mailing, changes, { | |
305 | 'api.mailing_job.create': 0 // note: exact match to API default | |
306 | }); | |
307 | return crmApi('Mailing', 'create', params).then(function (result) { | |
308 | if (result.id && !mailing.id) mailing.id = result.id; // no rollback, so update mailing.id | |
309 | _.extend(mailing, changes); // Perhaps we should reload mailing based on result? | |
310 | return result.values[result.id]; | |
311 | }); | |
705c61e9 TO |
312 | }, |
313 | ||
314 | // Immediately send a test message | |
beab9d1b TO |
315 | // @param mailing Object (per APIv3) |
316 | // @param testEmail string | |
317 | // @param testGroup int (id#) | |
318 | // @return Promise for a list of delivery reports | |
319 | sendTest: function(mailing, testEmail, testGroup) { | |
320 | var params = _.extend({}, mailing, { | |
43102e47 | 321 | // options: {force_rollback: 1}, // Test mailings include tracking features, so the mailing must be persistent |
beab9d1b TO |
322 | 'api.Mailing.send_test': { |
323 | mailing_id: '$value.id', | |
324 | test_email: testEmail, | |
325 | test_group: testGroup | |
326 | } | |
327 | }); | |
43102e47 TO |
328 | |
329 | // WORKAROUND: Mailing.create (aka CRM_Mailing_BAO_Mailing::create()) interprets scheduled_date | |
330 | // as an *intent* to schedule and creates tertiary records. Saving a draft with a scheduled_date | |
331 | // is therefore not allowed. Remove this after fixing Mailing.create's contract. | |
332 | delete params.scheduled_date; | |
333 | ||
beab9d1b TO |
334 | return crmApi('Mailing', 'create', params).then(function(result){ |
335 | if (result.id && !mailing.id) mailing.id = result.id; // no rollback, so update mailing.id | |
336 | return result.values[result.id]['api.Mailing.send_test'].values; | |
337 | }); | |
8dfd5110 TO |
338 | } |
339 | }; | |
340 | }); | |
8dfd5110 | 341 | })(angular, CRM.$, CRM._); |