CRM-15578 - Remove crmMailing
[civicrm-core.git] / js / angular-crmMailing2-directives.js
CommitLineData
5d72b4e2
TO
1(function (angular, $, _) {
2 var partialUrl = function (relPath) {
3 return CRM.resourceUrls['civicrm'] + '/partials/crmMailing2/' + relPath;
4 };
5
6 var crmMailing2 = angular.module('crmMailing2');
7
18da0e87
TO
8 // The following directives have the same simple implementation -- load
9 // a template and export a "mailing" object into scope.
10 var simpleBlocks = {
11 crmMailingBlockHeaderFooter: partialUrl('headerFooter.html'),
12 crmMailingBlockMailing: partialUrl('mailing.html'),
13 crmMailingBlockPreview: partialUrl('preview.html'),
14 crmMailingBlockPublication: partialUrl('publication.html'),
15 crmMailingBlockResponses: partialUrl('responses.html'),
62d2e387 16 crmMailingBlockRecipients: partialUrl('recipients.html'),
18da0e87
TO
17 crmMailingBlockReview: partialUrl('review.html'),
18 crmMailingBlockSchedule: partialUrl('schedule.html'),
19 crmMailingBlockSummary: partialUrl('summary.html'),
20 crmMailingBlockTracking: partialUrl('tracking.html'),
21 crmMailingBodyHtml: partialUrl('body_html.html'),
22 crmMailingBodyText: partialUrl('body_text.html')
23 };
24 _.each(simpleBlocks, function(templateUrl, directiveName){
25 crmMailing2.directive(directiveName, function ($parse) {
26 return {
27 scope: {
28 crmMailing: '@'
29 },
30 templateUrl: templateUrl,
31 link: function (scope, elm, attr) {
32 var model = $parse(attr.crmMailing);
33 scope.mailing = model(scope.$parent);
34 scope.crmMailingConst = CRM.crmMailing;
35 scope.ts = CRM.ts('CiviMail');
62d2e387 36 scope[directiveName] = attr[directiveName] ? scope.$parent.$eval(attr[directiveName]) : {};
18da0e87
TO
37 }
38 };
39 });
f8601d61
TO
40 });
41
0a993c89
TO
42 // Convert between a mailing "From Address" (mailing.from_name,mailing.from_email) and a unified label ("Name" <e@ma.il>)
43 // example: <span crm-mailing-from-address="myPlaceholder" crm-mailing="myMailing"><select ng-model="myPlaceholder.label"></select></span>
44 // NOTE: This really doesn't belong in a directive. I've tried (and failed) to make this work with a getterSetter binding, eg
45 // <select ng-model="mailing.convertFromAddress" ng-model-options="{getterSetter: true}">
46 crmMailing2.directive('crmMailingFromAddress', function ($parse, crmFromAddresses) {
47 return {
48 link: function (scope, element, attrs) {
49 var placeholder = attrs.crmMailingFromAddress;
50 var model = $parse(attrs.crmMailing);
51 var mailing = model(scope.$parent);
52 scope[placeholder] = {
53 label: crmFromAddresses.getByAuthorEmail(mailing.from_name, mailing.from_email, true).label
54 };
55 scope.$watch(placeholder + '.label', function (newValue) {
56 var addr = crmFromAddresses.getByLabel(newValue);
57 mailing.from_name = addr.author;
58 mailing.from_email = addr.email;
59 });
60 // FIXME: Shouldn't we also be watching mailing.from_name and mailing.from_email?
61 }
62 };
63 });
64
e47964f0
TO
65 // Represent a datetime field as if it were a radio ('schedule.mode') and a datetime ('schedule.datetime').
66 // example: <div crm-mailing-radio-date="mySchedule" crm-model="mailing.scheduled_date">...</div>
67 // FIXME: use ngModel instead of adhoc crmModel
68 crmMailing2.directive('crmMailingRadioDate', function ($parse) {
69 return {
70 link: function ($scope, element, attrs) {
71 var schedModel = $parse(attrs.crmModel);
72
73 var schedule = $scope[attrs.crmMailingRadioDate] = {
74 mode: 'now',
75 datetime: ''
76 };
77 var updateChildren = (function () {
78 var sched = schedModel($scope);
79 if (sched) {
80 schedule.mode = 'at';
81 schedule.datetime = sched;
82 }
83 else {
84 schedule.mode = 'now';
85 }
86 });
87 var updateParent = (function () {
88 switch (schedule.mode) {
89 case 'now':
90 schedModel.assign($scope, null);
91 break;
92 case 'at':
93 schedModel.assign($scope, schedule.datetime);
94 break;
95 default:
96 throw 'Unrecognized schedule mode: ' + schedule.mode;
97 }
98 });
99
100 $scope.$watch(attrs.crmModel, updateChildren);
101 $scope.$watch(attrs.crmMailingRadioDate + '.mode', updateParent);
102 $scope.$watch(attrs.crmMailingRadioDate + '.datetime', function (newValue, oldValue) {
103 // automatically switch mode based on datetime entry
104 if (oldValue != newValue) {
105 if (!newValue || newValue == " ") {
106 schedule.mode = 'now';
107 }
108 else {
109 schedule.mode = 'at';
110 }
111 }
112 updateParent();
113 });
114 }
115 };
116 });
117
f4f103fa 118 crmMailing2.directive('crmMailingReviewBool', function () {
47bacc20
TO
119 return {
120 scope: {
121 crmOn: '@',
122 crmTitle: '@'
123 },
124 template: '<span ng-class="spanClasses"><span class="icon" ng-class="iconClasses"></span>{{crmTitle}} </span>',
f4f103fa 125 link: function (scope, element, attrs) {
47bacc20
TO
126 function refresh() {
127 if (scope.$parent.$eval(attrs.crmOn)) {
128 scope.spanClasses = {'crmMailing2-active': true};
129 scope.iconClasses = {'ui-icon-check': true};
f4f103fa
TO
130 }
131 else {
47bacc20
TO
132 scope.spanClasses = {'crmMailing2-inactive': true};
133 scope.iconClasses = {'ui-icon-close': true};
134 }
135 scope.crmTitle = scope.$parent.$eval(attrs.crmTitle);
136 }
f4f103fa 137
47bacc20
TO
138 refresh();
139 scope.$parent.$watch(attrs.crmOn, refresh);
140 scope.$parent.$watch(attrs.crmTitle, refresh);
141 }
142 };
143 });
144
5d72b4e2
TO
145 // example: <input name="subject" /> <input crm-mailing-token crm-for="subject"/>
146 // WISHLIST: Instead of global CRM.crmMailing.mailTokens, accept token list as an input
3cc9c048 147 crmMailing2.directive('crmMailingToken', function () {
5d72b4e2 148 return {
3cc9c048 149 require: '^crmUiIdScope',
5d72b4e2
TO
150 scope: {
151 crmFor: '@'
152 },
153 template: '<input type="text" class="crmMailingToken" />',
3cc9c048 154 link: function (scope, element, attrs, crmUiIdCtrl) {
38737af8
TO
155 $(element).select2({
156 width: "10em",
5d72b4e2
TO
157 dropdownAutoWidth: true,
158 data: CRM.crmMailing.mailTokens,
159 placeholder: ts('Insert')
160 });
161 $(element).on('select2-selecting', function (e) {
3cc9c048 162 var id = crmUiIdCtrl.get(attrs.crmFor);
38737af8
TO
163 if (CKEDITOR.instances[id]) {
164 CKEDITOR.instances[id].insertText(e.val);
165 $(element).select2('close').select2('val', '');
166 CKEDITOR.instances[id].focus();
167 }
168 else {
3cc9c048 169 var crmForEl = $('#' + id);
38737af8
TO
170 var origVal = crmForEl.val();
171 var origPos = crmForEl[0].selectionStart;
172 var newVal = origVal.substring(0, origPos) + e.val + origVal.substring(origPos, origVal.length);
173 crmForEl.val(newVal);
174 var newPos = (origPos + e.val.length);
175 crmForEl[0].selectionStart = newPos;
176 crmForEl[0].selectionEnd = newPos;
5d72b4e2 177
38737af8
TO
178 $(element).select2('close').select2('val', '');
179 crmForEl.triggerHandler('change');
180 crmForEl.focus();
181 }
5d72b4e2
TO
182
183 e.preventDefault();
184 });
185 }
186 };
187 });
188
b0461279 189 // example: <select multiple crm-mailing-recipients crm-mailing="mymailing" crm-avail-groups="myGroups" crm-avail-mailings="myMailings"></select>
f8601d61 190 // FIXME: participate in ngModel's validation cycle
b0461279
TO
191 crmMailing2.directive('crmMailingRecipients', function () {
192 return {
193 restrict: 'AE',
194 scope: {
195 crmAvailGroups: '@', // available groups
196 crmAvailMailings: '@', // available mailings
197 crmMailing: '@' // the mailing for which we are choosing recipients
198 },
199 templateUrl: partialUrl('directive/recipients.html'),
200 link: function (scope, element, attrs) {
201 scope.mailing = scope.$parent.$eval(attrs.crmMailing);
202 scope.groups = scope.$parent.$eval(attrs.crmAvailGroups);
203 scope.mailings = scope.$parent.$eval(attrs.crmAvailMailings);
204
205 scope.ts = CRM.ts('CiviMail');
206
207 /// Convert MySQL date ("yyyy-mm-dd hh:mm:ss") to JS date object
208 scope.parseDate = function (date) {
f4f103fa 209 if (!angular.isString(date)) {
b0461279 210 return date;
f4f103fa 211 }
b0461279
TO
212 var p = date.split(/[\- :]/);
213 return new Date(p[0], p[1], p[2], p[3], p[4], p[5]);
214 };
215
216 /// Remove {value} from {array}
217 function arrayRemove(array, value) {
218 var idx = array.indexOf(value);
219 if (idx >= 0) {
220 array.splice(idx, 1);
221 }
222 }
223
224 // @param string id an encoded string like "4 civicrm_mailing include"
225 // @return Object keys: entity_id, entity_type, mode
226 function convertValueToObj(id) {
227 var a = id.split(" ");
228 return {entity_id: parseInt(a[0]), entity_type: a[1], mode: a[2]};
229 }
230
231 // @param Object mailing
232 // @return array list of values like "4 civicrm_mailing include"
233 function convertMailingToValues(mailing) {
234 var r = [];
235 angular.forEach(mailing.groups.include, function (v) {
236 r.push(v + " civicrm_group include");
237 });
238 angular.forEach(mailing.groups.exclude, function (v) {
239 r.push(v + " civicrm_group exclude");
240 });
241 angular.forEach(mailing.mailings.include, function (v) {
242 r.push(v + " civicrm_mailing include");
243 });
244 angular.forEach(mailing.mailings.exclude, function (v) {
245 r.push(v + " civicrm_mailing exclude");
246 });
247 return r;
248 }
249
250 // Update $(element) view based on latest data
251 function refreshUI() {
252 $(element).select2('val', convertMailingToValues(scope.mailing));
253 }
254
255 /// @return string HTML representingn an option
256 function formatItem(item) {
257 if (!item.id) {
258 // return `text` for optgroup
259 return item.text;
260 }
261 var option = convertValueToObj(item.id);
262 var icon = (option.entity_type === 'civicrm_mailing') ? 'EnvelopeIn.gif' : 'group.png';
263 var spanClass = (option.mode == 'exclude') ? 'crmMailing2-exclude' : 'crmMailing2-include';
264 return "<img src='../../sites/all/modules/civicrm/i/" + icon + "' height=12 width=12 /> <span class='" + spanClass + "'>" + item.text + "</span>";
265 }
266
267 $(element).select2({
268 dropdownAutoWidth: true,
269 placeholder: "Groups or Past Recipients",
270 formatResult: formatItem,
271 formatSelection: formatItem,
272 escapeMarkup: function (m) {
273 return m;
274 },
275 });
276
277 $(element).on('select2-selecting', function (e) {
278 var option = convertValueToObj(e.val);
279 var typeKey = option.entity_type == 'civicrm_mailing' ? 'mailings' : 'groups';
280 if (option.mode == 'exclude') {
281 scope.mailing[typeKey].exclude.push(option.entity_id);
282 arrayRemove(scope.mailing[typeKey].include, option.entity_id);
f4f103fa
TO
283 }
284 else {
b0461279
TO
285 scope.mailing[typeKey].include.push(option.entity_id);
286 arrayRemove(scope.mailing[typeKey].exclude, option.entity_id);
287 }
288 scope.$apply();
289 $(element).select2('close');
290 e.preventDefault();
291 });
292
293 $(element).on("select2-removing", function (e) {
294 var option = convertValueToObj(e.val);
295 var typeKey = option.entity_type == 'civicrm_mailing' ? 'mailings' : 'groups';
f4f103fa 296 scope.$parent.$apply(function () {
89a50c67
TO
297 arrayRemove(scope.mailing[typeKey][option.mode], option.entity_id);
298 });
b0461279
TO
299 e.preventDefault();
300 });
301
302 scope.$watchCollection(attrs.crmMailing + ".groups.include", refreshUI);
303 scope.$watchCollection(attrs.crmMailing + ".groups.exclude", refreshUI);
304 scope.$watchCollection(attrs.crmMailing + ".mailings.include", refreshUI);
305 scope.$watchCollection(attrs.crmMailing + ".mailings.exclude", refreshUI);
306 setTimeout(refreshUI, 50);
307 }
308 };
309 });
310
5d72b4e2 311})(angular, CRM.$, CRM._);