add code comments
[civicrm-core.git] / js / angular-crmMailingAB.js
1 (function (angular, $, _) {
2
3 var partialUrl = function (relPath, module) {
4 if (!module) {
5 module = 'crmMailingAB';
6 }
7 return CRM.resourceUrls.civicrm + '/partials/' + module + '/' + relPath;
8 };
9
10 angular.module('crmMailingAB', ['ngRoute', 'ui.utils', 'ngSanitize', 'crmUi', 'crmAttachment', 'crmMailing', 'crmD3']);
11 angular.module('crmMailingAB').config([
12 '$routeProvider',
13 function ($routeProvider) {
14 $routeProvider.when('/abtest', {
15 templateUrl: partialUrl('list.html'),
16 controller: 'CrmMailingABListCtrl',
17 resolve: {
18 mailingABList: function ($route, crmApi) {
19 return crmApi('MailingAB', 'get', {rowCount: 0});
20 }
21 }
22 });
23 $routeProvider.when('/abtest/:id', {
24 templateUrl: partialUrl('edit.html'),
25 controller: 'CrmMailingABEditCtrl',
26 resolve: {
27 abtest: function ($route, CrmMailingAB) {
28 var abtest = new CrmMailingAB($route.current.params.id == 'new' ? null : $route.current.params.id);
29 return abtest.load();
30 }
31 }
32 });
33 $routeProvider.when('/abtest/:id/report', {
34 templateUrl: partialUrl('report.html'),
35 controller: 'CrmMailingABReportCtrl',
36 resolve: {
37 abtest: function ($route, CrmMailingAB) {
38 var abtest = new CrmMailingAB($route.current.params.id);
39 return abtest.load();
40 }
41 }
42 });
43 }
44 ]);
45
46 angular.module('crmMailingAB').controller('CrmMailingABListCtrl', function ($scope, mailingABList, crmMailingABCriteria, crmMailingABStatus) {
47 var ts = $scope.ts = CRM.ts('CiviMail');
48 $scope.mailingABList = mailingABList.values;
49 $scope.crmMailingABCriteria = crmMailingABCriteria;
50 $scope.crmMailingABStatus = crmMailingABStatus;
51 });
52
53 angular.module('crmMailingAB').controller('CrmMailingABEditCtrl', function ($scope, abtest, crmMailingABCriteria, crmMailingMgr, crmMailingPreviewMgr, crmStatus, $q, $location) {
54 $scope.abtest = abtest;
55 var ts = $scope.ts = CRM.ts('CiviMail');
56 $scope.crmMailingABCriteria = crmMailingABCriteria;
57 $scope.crmMailingConst = CRM.crmMailing;
58 $scope.partialUrl = partialUrl;
59
60 $scope.isSubmitted = function isSubmitted() {
61 return _.size(abtest.mailings.a.jobs) > 0 || _.size(abtest.mailings.b.jobs) > 0;
62 };
63
64 $scope.sync = function sync() {
65 abtest.mailings.a.name = ts('Test A (%1)', {1: abtest.ab.name});
66 abtest.mailings.b.name = ts('Test B (%1)', {1: abtest.ab.name});
67 abtest.mailings.c.name = ts('Winner (%1)', {1: abtest.ab.name});
68
69 var criteria = crmMailingABCriteria.get(abtest.ab.testing_criteria_id);
70 if (criteria) {
71 // TODO review fields exposed in UI and make sure the sync rules match
72 switch (criteria.name) {
73 case 'Subject lines':
74 crmMailingMgr.mergeInto(abtest.mailings.b, abtest.mailings.a, [
75 'name',
76 'groups',
77 'mailings',
78 'subject'
79 ]);
80 break;
81 case 'From names':
82 crmMailingMgr.mergeInto(abtest.mailings.b, abtest.mailings.a, [
83 'name',
84 'groups',
85 'mailings',
86 'from_name',
87 'from_email'
88 ]);
89 break;
90 case 'Two different emails':
91 crmMailingMgr.mergeInto(abtest.mailings.b, abtest.mailings.a, [
92 'name',
93 'groups',
94 'mailings',
95 'subject',
96 'from_name',
97 'from_email',
98 'body_html',
99 'body_text'
100 ]);
101 break;
102 default:
103 throw "Unrecognized testing_criteria";
104 }
105 }
106 crmMailingMgr.mergeInto(abtest.mailings.c, abtest.mailings.a, ['name']);
107 return $q.when(true);
108 };
109
110 // @return Promise
111 $scope.save = function save() {
112 $scope.sync();
113 return crmStatus({start: ts('Saving...'), success: ts('Saved')}, abtest.save().then(updateUrl));
114 };
115
116 // @return Promise
117 $scope.previewMailing = function previewMailing(mailingName, mode) {
118 $scope.sync();
119 return crmMailingPreviewMgr.preview(abtest.mailings[mailingName], mode);
120 };
121
122 // @return Promise
123 $scope.sendTest = function sendTest(mailingName, recipient) {
124 $scope.sync();
125 return crmStatus({start: ts('Saving...'), success: ''}, abtest.save().then(updateUrl))
126 .then(function () {
127 crmMailingPreviewMgr.sendTest(abtest.mailings[mailingName], recipient);
128 });
129 };
130
131 // @return Promise
132 $scope.delete = function () {
133 return crmStatus({start: ts('Deleting...'), success: ts('Deleted')}, abtest.delete().then(leave));
134 };
135
136 // @return Promise
137 $scope.submit = function submit() {
138 $scope.sync();
139 return crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
140 .then(function () {
141 return crmStatus({start: ts('Submitting...'), success: ts('Submitted')}, abtest.submitTest());
142 // Note: We're going to leave, so we don't care that submit() modifies several server-side records.
143 // If we stayed on this page, then we'd care about updating and call: abtest.submitTest().then(...abtest.load()...)
144 })
145 .then(leave);
146 };
147
148 function leave() {
149 $location.path('abtest');
150 $location.replace();
151 }
152
153 function updateCriteriaName() {
154 var criteria = crmMailingABCriteria.get($scope.abtest.ab.testing_criteria_id);
155 $scope.criteriaName = criteria ? criteria.name : null;
156 }
157
158 // Transition URL "/abtest/new" => "/abtest/123"
159 function updateUrl() {
160 var parts = $location.path().split('/'); // e.g. "/abtest/new" or "/abtest/123/wizard"
161 if (parts[2] != $scope.abtest.ab.id) {
162 parts[2] = $scope.abtest.ab.id;
163 $location.path(parts.join('/'));
164 $location.replace();
165 // FIXME: Angular unnecessarily refreshes UI
166 // WARNING: Changing the URL triggers a full reload. Any pending AJAX operations
167 // could be inconsistently applied. Run updateUrl() after other changes complete.
168 }
169 }
170
171 // initialize
172 updateCriteriaName();
173 $scope.sync();
174 $scope.$watch('abtest.ab.testing_criteria_id', updateCriteriaName);
175 });
176
177 angular.module('crmMailingAB').controller('CrmMailingABReportCtrl', function ($scope, abtest, crmApi, crmMailingPreviewMgr, dialogService) {
178 var ts = $scope.ts = CRM.ts('CiviMail');
179
180 $scope.abtest = abtest;
181
182 $scope.stats = {};
183 crmApi('Mailing', 'stats', {mailing_id: abtest.ab.mailing_id_a}).then(function(data){
184 $scope.stats.a = data.values[abtest.ab.mailing_id_a];
185 });
186 crmApi('Mailing', 'stats', {mailing_id: abtest.ab.mailing_id_b}).then(function(data){
187 $scope.stats.b = data.values[abtest.ab.mailing_id_b];
188 });
189 crmApi('Mailing', 'stats', {mailing_id: abtest.ab.mailing_id_c}).then(function(data){
190 $scope.stats.c = data.values[abtest.ab.mailing_id_c];
191 });
192
193 $scope.previewMailing = function previewMailing(mailingName, mode) {
194 return crmMailingPreviewMgr.preview(abtest.mailings[mailingName], mode);
195 };
196 $scope.selectWinner = function selectWinner(mailingName) {
197 var model = {
198 abtest: abtest,
199 mailingName: mailingName
200 };
201 var options = {
202 autoOpen: false,
203 modal: true,
204 title: ts('Select Winner (%1)', {
205 1: mailingName.toUpperCase()
206 })
207 };
208 return dialogService.open('selectWinnerDialog', partialUrl('selectWinner.html'), model, options);
209 };
210 });
211
212
213 angular.module('crmMailingAB').controller('CrmMailingABWinnerDialogCtrl', function ($scope, $timeout, dialogService, crmMailingMgr, crmStatus) {
214 var ts = $scope.ts = CRM.ts('CiviMail');
215 var abtest = $scope.abtest = $scope.model.abtest;
216 var mailingName = $scope.model.mailingName;
217
218 var titles = {a: ts('Mailing A'), b: ts('Mailing B')};
219 $scope.mailingTitle = titles[mailingName];
220
221 function init() {
222 // When using dialogService with a button bar, the major button actions
223 // need to be registered with the dialog widget (and not embedded in
224 // the body of the dialog).
225 var buttons = {};
226 buttons[ts('Select Winner')] = function () {
227 crmMailingMgr.mergeInto(abtest.mailings.c, abtest.mailings[mailingName], [
228 'name',
229 'groups',
230 'mailings',
231 'scheduled_date'
232 ]);
233 crmStatus({start: ts('Saving...'), success: ''}, abtest.save())
234 .then(function () {
235 return crmStatus({start: ts('Submitting...'), success: ts('Submitted')},
236 abtest.submitFinal().then(function(){
237 return abtest.load();
238 }));
239 })
240 .then(function(){
241 dialogService.close('selectWinnerDialog', abtest);
242 });
243 };
244 buttons[ts('Cancel')] = function () {
245 dialogService.cancel('selectWinnerDialog');
246 };
247 dialogService.setButtons('selectWinnerDialog', buttons);
248 }
249
250 $timeout(init);
251 });
252
253 })(angular, CRM.$, CRM._);