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