1 (function (angular
, $, _
) {
2 var partialUrl = function (relPath
) {
3 return CRM
.resourceUrls
['civicrm'] + '/partials/crmMailing2/' + relPath
;
6 var crmMailing2
= angular
.module('crmMailing2');
8 // The following directives have the same simple implementation -- load
9 // a template and export a "mailing" object into scope.
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')
23 _
.each(simpleBlocks
, function(templateUrl
, directiveName
){
24 crmMailing2
.directive(directiveName
, function ($parse
) {
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');
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
) {
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
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
;
58 // FIXME: Shouldn't we also be watching mailing.from_name and mailing.from_email?
63 crmMailing2
.directive('crmMailingReviewBool', function () {
69 template
: '<span ng-class="spanClasses"><span class="icon" ng-class="iconClasses"></span>{{crmTitle}} </span>',
70 link: function (scope
, element
, attrs
) {
72 if (scope
.$parent
.$eval(attrs
.crmOn
)) {
73 scope
.spanClasses
= {'crmMailing2-active': true};
74 scope
.iconClasses
= {'ui-icon-check': true};
77 scope
.spanClasses
= {'crmMailing2-inactive': true};
78 scope
.iconClasses
= {'ui-icon-close': true};
80 scope
.crmTitle
= scope
.$parent
.$eval(attrs
.crmTitle
);
84 scope
.$parent
.$watch(attrs
.crmOn
, refresh
);
85 scope
.$parent
.$watch(attrs
.crmTitle
, refresh
);
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
92 crmMailing2
.directive('crmMailingToken', function (crmUiId
) {
97 template
: '<input type="text" class="crmMailingToken" />',
98 link: function (scope
, element
, attrs
) {
99 // 1. Find the corresponding input element (crmFor)
101 var form
= $(element
).closest('form');
102 var crmForEl
= $('input[name="' + attrs
.crmFor
+ '"],textarea[name="' + attrs
.crmFor
+ '"]', form
);
103 if (form
.length
!= 1 || crmForEl
.length
!= 1) {
105 console
.log('crmMailingToken cannot be matched to input element. Expected to find one form and one input.', form
.length
, crmForEl
.length
);
110 // 2. Setup the token selector
113 dropdownAutoWidth
: true,
114 data
: CRM
.crmMailing
.mailTokens
,
115 placeholder
: ts('Insert')
117 $(element
).on('select2-selecting', function (e
) {
118 var id
= crmUiId(crmForEl
);
119 if (CKEDITOR
.instances
[id
]) {
120 CKEDITOR
.instances
[id
].insertText(e
.val
);
121 $(element
).select2('close').select2('val', '');
122 CKEDITOR
.instances
[id
].focus();
125 var origVal
= crmForEl
.val();
126 var origPos
= crmForEl
[0].selectionStart
;
127 var newVal
= origVal
.substring(0, origPos
) + e
.val
+ origVal
.substring(origPos
, origVal
.length
);
128 crmForEl
.val(newVal
);
129 var newPos
= (origPos
+ e
.val
.length
);
130 crmForEl
[0].selectionStart
= newPos
;
131 crmForEl
[0].selectionEnd
= newPos
;
133 $(element
).select2('close').select2('val', '');
134 crmForEl
.triggerHandler('change');
144 // example: <select multiple crm-mailing-recipients crm-mailing="mymailing" crm-avail-groups="myGroups" crm-avail-mailings="myMailings"></select>
145 // FIXME: participate in ngModel's validation cycle
146 crmMailing2
.directive('crmMailingRecipients', function () {
150 crmAvailGroups
: '@', // available groups
151 crmAvailMailings
: '@', // available mailings
152 crmMailing
: '@' // the mailing for which we are choosing recipients
154 templateUrl
: partialUrl('directive/recipients.html'),
155 link: function (scope
, element
, attrs
) {
156 scope
.mailing
= scope
.$parent
.$eval(attrs
.crmMailing
);
157 scope
.groups
= scope
.$parent
.$eval(attrs
.crmAvailGroups
);
158 scope
.mailings
= scope
.$parent
.$eval(attrs
.crmAvailMailings
);
160 scope
.ts
= CRM
.ts('CiviMail');
162 /// Convert MySQL date ("yyyy-mm-dd hh:mm:ss") to JS date object
163 scope
.parseDate = function (date
) {
164 if (!angular
.isString(date
)) {
167 var p
= date
.split(/[\- :]/);
168 return new Date(p
[0], p
[1], p
[2], p
[3], p
[4], p
[5]);
171 /// Remove {value} from {array}
172 function arrayRemove(array
, value
) {
173 var idx
= array
.indexOf(value
);
175 array
.splice(idx
, 1);
179 // @param string id an encoded string like "4 civicrm_mailing include"
180 // @return Object keys: entity_id, entity_type, mode
181 function convertValueToObj(id
) {
182 var a
= id
.split(" ");
183 return {entity_id
: parseInt(a
[0]), entity_type
: a
[1], mode
: a
[2]};
186 // @param Object mailing
187 // @return array list of values like "4 civicrm_mailing include"
188 function convertMailingToValues(mailing
) {
190 angular
.forEach(mailing
.groups
.include
, function (v
) {
191 r
.push(v
+ " civicrm_group include");
193 angular
.forEach(mailing
.groups
.exclude
, function (v
) {
194 r
.push(v
+ " civicrm_group exclude");
196 angular
.forEach(mailing
.mailings
.include
, function (v
) {
197 r
.push(v
+ " civicrm_mailing include");
199 angular
.forEach(mailing
.mailings
.exclude
, function (v
) {
200 r
.push(v
+ " civicrm_mailing exclude");
205 // Update $(element) view based on latest data
206 function refreshUI() {
207 $(element
).select2('val', convertMailingToValues(scope
.mailing
));
210 /// @return string HTML representingn an option
211 function formatItem(item
) {
213 // return `text` for optgroup
216 var option
= convertValueToObj(item
.id
);
217 var icon
= (option
.entity_type
=== 'civicrm_mailing') ? 'EnvelopeIn.gif' : 'group.png';
218 var spanClass
= (option
.mode
== 'exclude') ? 'crmMailing2-exclude' : 'crmMailing2-include';
219 return "<img src='../../sites/all/modules/civicrm/i/" + icon
+ "' height=12 width=12 /> <span class='" + spanClass
+ "'>" + item
.text
+ "</span>";
223 dropdownAutoWidth
: true,
224 placeholder
: "Groups or Past Recipients",
225 formatResult
: formatItem
,
226 formatSelection
: formatItem
,
227 escapeMarkup: function (m
) {
232 $(element
).on('select2-selecting', function (e
) {
233 var option
= convertValueToObj(e
.val
);
234 var typeKey
= option
.entity_type
== 'civicrm_mailing' ? 'mailings' : 'groups';
235 if (option
.mode
== 'exclude') {
236 scope
.mailing
[typeKey
].exclude
.push(option
.entity_id
);
237 arrayRemove(scope
.mailing
[typeKey
].include
, option
.entity_id
);
240 scope
.mailing
[typeKey
].include
.push(option
.entity_id
);
241 arrayRemove(scope
.mailing
[typeKey
].exclude
, option
.entity_id
);
244 $(element
).select2('close');
248 $(element
).on("select2-removing", function (e
) {
249 var option
= convertValueToObj(e
.val
);
250 var typeKey
= option
.entity_type
== 'civicrm_mailing' ? 'mailings' : 'groups';
251 scope
.$parent
.$apply(function () {
252 arrayRemove(scope
.mailing
[typeKey
][option
.mode
], option
.entity_id
);
257 scope
.$watchCollection(attrs
.crmMailing
+ ".groups.include", refreshUI
);
258 scope
.$watchCollection(attrs
.crmMailing
+ ".groups.exclude", refreshUI
);
259 scope
.$watchCollection(attrs
.crmMailing
+ ".mailings.include", refreshUI
);
260 scope
.$watchCollection(attrs
.crmMailing
+ ".mailings.exclude", refreshUI
);
261 setTimeout(refreshUI
, 50);
266 })(angular
, CRM
.$, CRM
._
);