Commit | Line | Data |
---|---|---|
4aef704e | 1 | <?php |
4aef704e | 2 | /* |
3 | +--------------------------------------------------------------------+ | |
a30c801b | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
4aef704e | 5 | | | |
a30c801b TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
4aef704e | 9 | +--------------------------------------------------------------------+ |
d25dd0ee | 10 | */ |
4aef704e | 11 | |
12 | /** | |
13 | * | |
14 | * APIv3 functions for registering/processing mailing ab testing events. | |
15 | * | |
16 | * @package CiviCRM_APIv3 | |
4aef704e | 17 | */ |
18 | ||
8b06123b TO |
19 | /** |
20 | * @param array $spec | |
21 | */ | |
22 | function _civicrm_api3_mailing_a_b_create_spec(&$spec) { | |
23 | $spec['created_date']['api.default'] = 'now'; | |
24 | $spec['created_id']['api.required'] = 1; | |
25 | $spec['created_id']['api.default'] = 'user_contact_id'; | |
a63d3a16 | 26 | $spec['domain_id']['api.default'] = CRM_Core_Config::domainID(); |
8b06123b TO |
27 | } |
28 | ||
4aef704e | 29 | /** |
1747ab99 | 30 | * Handle a create mailing ab testing. |
4aef704e | 31 | * |
32 | * @param array $params | |
4aef704e | 33 | * |
a6c01b45 | 34 | * @return array |
72b3a70c | 35 | * API Success Array |
4aef704e | 36 | */ |
fd843187 | 37 | function civicrm_api3_mailing_a_b_create($params) { |
a25b46e9 | 38 | return _civicrm_api3_basic_create(_civicrm_api3_get_BAO(__FUNCTION__), $params, 'MailingAB'); |
4aef704e | 39 | } |
40 | ||
41 | /** | |
42 | * Handle a delete event. | |
43 | * | |
44 | * @param array $params | |
4aef704e | 45 | * |
a6c01b45 | 46 | * @return array |
72b3a70c | 47 | * API Success Array |
4aef704e | 48 | */ |
fd843187 | 49 | function civicrm_api3_mailing_a_b_delete($params) { |
4aef704e | 50 | return _civicrm_api3_basic_delete(_civicrm_api3_get_BAO(__FUNCTION__), $params); |
51 | } | |
52 | ||
53 | /** | |
54 | * Handle a get event. | |
55 | * | |
56 | * @param array $params | |
dc64d047 | 57 | * |
4aef704e | 58 | * @return array |
59 | */ | |
fd843187 | 60 | function civicrm_api3_mailing_a_b_get($params) { |
4aef704e | 61 | return _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), $params); |
62 | } | |
63 | ||
7811a84b | 64 | /** |
1747ab99 | 65 | * Adjust Metadata for submit action. |
7811a84b | 66 | * |
0aa0303c EM |
67 | * The metadata is used for setting defaults, documentation & validation. |
68 | * | |
481259e3 | 69 | * @param array $spec |
b081365f | 70 | * Array of parameters determined by getfields. |
7811a84b | 71 | */ |
481259e3 | 72 | function _civicrm_api3_mailing_a_b_submit_spec(&$spec) { |
768c558c TO |
73 | $mailingFields = CRM_Mailing_DAO_Mailing::fields(); |
74 | $mailingAbFields = CRM_Mailing_DAO_MailingAB::fields(); | |
75 | $spec['id'] = $mailingAbFields['id']; | |
76 | $spec['status'] = $mailingAbFields['status']; | |
77 | $spec['scheduled_date'] = $mailingFields['scheduled_date']; | |
78 | $spec['approval_date'] = $mailingFields['approval_date']; | |
79 | $spec['approval_status_id'] = $mailingFields['approval_status_id']; | |
80 | $spec['approval_note'] = $mailingFields['approval_note']; | |
9ee73e80 TO |
81 | $spec['winner_id'] = [ |
82 | 'name' => 'winner_id', | |
83 | 'type' => 1, | |
84 | 'title' => 'Winner ID', | |
85 | 'description' => 'The experimental mailing with the best results. If specified, values are copied to the final mailing.', | |
86 | 'localizable' => 0, | |
87 | ]; | |
768c558c TO |
88 | // Note: we'll pass through approval_* fields to the underlying mailing, but they may be ignored |
89 | // if the user doesn't have suitable permission. If separate approvals are required, they must be provided | |
90 | // outside the A/B Test UI. | |
7811a84b | 91 | } |
92 | ||
93 | /** | |
1747ab99 | 94 | * Send A/B mail to A/B recipients respectively. |
7811a84b | 95 | * |
96 | * @param array $params | |
1747ab99 | 97 | * |
7811a84b | 98 | * @return array |
768c558c | 99 | * @throws API_Exception |
7811a84b | 100 | */ |
768c558c | 101 | function civicrm_api3_mailing_a_b_submit($params) { |
cf8f0fff | 102 | civicrm_api3_verify_mandatory($params, 'CRM_Mailing_DAO_MailingAB', ['id', 'status']); |
768c558c TO |
103 | |
104 | if (!isset($params['scheduled_date']) && !isset($updateParams['approval_date'])) { | |
105 | throw new API_Exception("Missing parameter scheduled_date and/or approval_date"); | |
106 | } | |
7811a84b | 107 | |
768c558c TO |
108 | $dao = new CRM_Mailing_DAO_MailingAB(); |
109 | $dao->id = $params['id']; | |
110 | if (!$dao->find(TRUE)) { | |
111 | throw new API_Exception("Failed to locate A/B test by ID"); | |
7811a84b | 112 | } |
768c558c TO |
113 | if (empty($dao->mailing_id_a) || empty($dao->mailing_id_b) || empty($dao->mailing_id_c)) { |
114 | throw new API_Exception("Missing mailing IDs for A/B test"); | |
7811a84b | 115 | } |
116 | ||
cf8f0fff | 117 | $submitParams = CRM_Utils_Array::subset($params, [ |
768c558c TO |
118 | 'scheduled_date', |
119 | 'approval_date', | |
120 | 'approval_note', | |
121 | 'approval_status_id', | |
cf8f0fff | 122 | ]); |
768c558c TO |
123 | |
124 | switch ($params['status']) { | |
125 | case 'Testing': | |
126 | if (!empty($dao->status) && $dao->status != 'Draft') { | |
127 | throw new API_Exception("Cannot transition to state 'Testing'"); | |
128 | } | |
cf8f0fff | 129 | civicrm_api3('Mailing', 'submit', $submitParams + [ |
7c31ae57 SL |
130 | 'id' => $dao->mailing_id_a, |
131 | '_skip_evil_bao_auto_recipients_' => 0, | |
132 | ]); | |
cf8f0fff | 133 | civicrm_api3('Mailing', 'submit', $submitParams + [ |
7c31ae57 SL |
134 | 'id' => $dao->mailing_id_b, |
135 | '_skip_evil_bao_auto_recipients_' => 1, | |
136 | ]); | |
768c558c TO |
137 | CRM_Mailing_BAO_MailingAB::distributeRecipients($dao); |
138 | break; | |
7811a84b | 139 | |
768c558c TO |
140 | case 'Final': |
141 | if ($dao->status != 'Testing') { | |
142 | throw new API_Exception("Cannot transition to state 'Final'"); | |
143 | } | |
9ee73e80 TO |
144 | if (!empty($params['winner_id'])) { |
145 | _civicrm_api3_mailing_a_b_fill_winner($params['winner_id'], $dao->mailing_id_c); | |
146 | } | |
cf8f0fff | 147 | civicrm_api3('Mailing', 'submit', $submitParams + [ |
7c31ae57 SL |
148 | 'id' => $dao->mailing_id_c, |
149 | '_skip_evil_bao_auto_recipients_' => 1, | |
150 | ]); | |
768c558c TO |
151 | break; |
152 | ||
153 | default: | |
154 | throw new API_Exception("Unrecognized submission status"); | |
7811a84b | 155 | } |
156 | ||
cf8f0fff | 157 | return civicrm_api3('MailingAB', 'create', [ |
768c558c TO |
158 | 'id' => $dao->id, |
159 | 'status' => $params['status'], | |
cf8f0fff | 160 | 'options' => [ |
768c558c | 161 | 'reload' => 1, |
cf8f0fff CW |
162 | ], |
163 | ]); | |
467cd00c | 164 | } |
165 | ||
9ee73e80 TO |
166 | /** |
167 | * @param int $winner_id | |
168 | * The experimental mailing chosen as the "winner". | |
169 | * @param int $final_id | |
170 | * The final mailing which should imitate the "winner". | |
171 | * @throws \API_Exception | |
172 | */ | |
173 | function _civicrm_api3_mailing_a_b_fill_winner($winner_id, $final_id) { | |
174 | $copyFields = [ | |
175 | // 'id', | |
176 | // 'name', | |
177 | 'campaign_id', | |
178 | 'from_name', | |
179 | 'from_email', | |
180 | 'replyto_email', | |
181 | 'subject', | |
182 | 'dedupe_email', | |
183 | // 'recipients', | |
184 | 'body_html', | |
185 | 'body_text', | |
186 | 'footer_id', | |
187 | 'header_id', | |
188 | 'visibility', | |
189 | 'url_tracking', | |
190 | 'dedupe_email', | |
191 | 'forward_replies', | |
192 | 'auto_responder', | |
193 | 'open_tracking', | |
194 | 'override_verp', | |
195 | 'optout_id', | |
196 | 'reply_id', | |
197 | 'resubscribe_id', | |
d683bc99 | 198 | 'unsubscribe_id', |
860d9f81 | 199 | 'template_type', |
bb597a93 LS |
200 | 'template_options', |
201 | 'language', | |
9ee73e80 TO |
202 | ]; |
203 | $f = CRM_Utils_SQL_Select::from('civicrm_mailing') | |
204 | ->where('id = #id', ['id' => $winner_id]) | |
205 | ->select($copyFields) | |
206 | ->execute() | |
207 | ->fetchAll(); | |
208 | if (count($f) !== 1) { | |
209 | throw new API_Exception('Invalid winner_id'); | |
210 | } | |
211 | foreach ($f as $winner) { | |
212 | civicrm_api3('Mailing', 'create', $winner + [ | |
213 | 'id' => $final_id, | |
214 | '_skip_evil_bao_auto_recipients_' => 1, | |
215 | ]); | |
216 | } | |
217 | } | |
218 | ||
467cd00c | 219 | /** |
1747ab99 | 220 | * Adjust Metadata for graph_stats action. |
467cd00c | 221 | * |
0aa0303c EM |
222 | * The metadata is used for setting defaults, documentation & validation. |
223 | * | |
cf470720 | 224 | * @param array $params |
b081365f | 225 | * Array of parameters determined by getfields. |
467cd00c | 226 | */ |
227 | function _civicrm_api3_mailing_a_b_graph_stats_spec(&$params) { | |
cf8f0fff | 228 | $params['criteria'] = [ |
d142432b EM |
229 | 'title' => 'Criteria', |
230 | 'default' => 'Open', | |
231 | 'type' => CRM_Utils_Type::T_STRING, | |
cf8f0fff | 232 | ]; |
d142432b | 233 | |
360aaa75 TO |
234 | // mailing_ab_winner_criteria |
235 | $params['target_date']['title'] = 'Target Date'; | |
236 | $params['target_date']['type'] = CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME; | |
cf8f0fff | 237 | $params['split_count'] = [ |
d142432b EM |
238 | 'title' => 'Split Count', |
239 | 'api.default' => 6, | |
240 | 'type' => CRM_Utils_Type::T_INT, | |
cf8f0fff | 241 | ]; |
bd6658bd | 242 | $params['split_count_select']['title'] = 'Split Count Select'; |
202ebbab | 243 | $params['split_count_select']['api.required'] = 1; |
360aaa75 | 244 | $params['target_url']['title'] = 'Target URL'; |
202ebbab | 245 | } |
467cd00c | 246 | |
247 | /** | |
1747ab99 | 248 | * Send graph detail for A/B tests mail. |
467cd00c | 249 | * |
250 | * @param array $params | |
1747ab99 | 251 | * |
467cd00c | 252 | * @return array |
360aaa75 | 253 | * @throws API_Exception |
467cd00c | 254 | */ |
255 | function civicrm_api3_mailing_a_b_graph_stats($params) { | |
202ebbab | 256 | civicrm_api3_verify_mandatory($params, |
257 | 'CRM_Mailing_DAO_MailingAB', | |
cf8f0fff | 258 | ['id'], |
202ebbab | 259 | FALSE |
260 | ); | |
467cd00c | 261 | |
cf8f0fff | 262 | $defaults = [ |
360aaa75 TO |
263 | 'criteria' => 'Open', |
264 | 'target_date' => CRM_Utils_Time::getTime('YmdHis'), | |
265 | 'split_count' => 6, | |
266 | 'split_count_select' => 1, | |
cf8f0fff | 267 | ]; |
360aaa75 | 268 | $params = array_merge($defaults, $params); |
202ebbab | 269 | |
cf8f0fff CW |
270 | $mailingAB = civicrm_api3('MailingAB', 'getsingle', ['id' => $params['id']]); |
271 | $graphStats = []; | |
272 | $ABFormat = ['A' => 'mailing_id_a', 'B' => 'mailing_id_b']; | |
467cd00c | 273 | |
202ebbab | 274 | foreach ($ABFormat as $name => $column) { |
360aaa75 TO |
275 | switch (strtolower($params['criteria'])) { |
276 | case 'open': | |
768c558c TO |
277 | $result = CRM_Mailing_Event_BAO_Opened::getRows($mailingAB['mailing_id_a'], NULL, TRUE, 0, 1, "civicrm_mailing_event_opened.time_stamp ASC"); |
278 | $startDate = CRM_Utils_Date::processDate($result[0]['date']); | |
360aaa75 TO |
279 | $targetDate = CRM_Utils_Date::processDate($params['target_date']); |
280 | $dateDuration = round(round(strtotime($targetDate) - strtotime($startDate)) / $params['split_count']); | |
202ebbab | 281 | $toDate = strtotime($startDate) + ($dateDuration * $params['split_count_select']); |
282 | $toDate = date('YmdHis', $toDate); | |
cf8f0fff CW |
283 | $graphStats[$name] = [ |
284 | $params['split_count_select'] => [ | |
202ebbab | 285 | 'count' => CRM_Mailing_Event_BAO_Opened::getTotalCount($mailingAB[$column], NULL, TRUE, $toDate), |
21dfd5f5 | 286 | 'time' => CRM_Utils_Date::customFormat($toDate), |
cf8f0fff CW |
287 | ], |
288 | ]; | |
202ebbab | 289 | break; |
ea100cb5 | 290 | |
360aaa75 | 291 | case 'total unique clicks': |
202ebbab | 292 | $result = CRM_Mailing_Event_BAO_TrackableURLOpen::getRows($mailingAB['mailing_id_a'], NULL, TRUE, 0, 1, "civicrm_mailing_event_trackable_url_open.time_stamp ASC"); |
293 | $startDate = CRM_Utils_Date::processDate($result[0]['date']); | |
360aaa75 TO |
294 | $targetDate = CRM_Utils_Date::processDate($params['target_date']); |
295 | $dateDuration = round(abs(strtotime($targetDate) - strtotime($startDate)) / $params['split_count']); | |
202ebbab | 296 | $toDate = strtotime($startDate) + ($dateDuration * $params['split_count_select']); |
297 | $toDate = date('YmdHis', $toDate); | |
cf8f0fff CW |
298 | $graphStats[$name] = [ |
299 | $params['split_count_select'] => [ | |
202ebbab | 300 | 'count' => CRM_Mailing_Event_BAO_TrackableURLOpen::getTotalCount($params['mailing_id'], NULL, FALSE, NULL, $toDate), |
21dfd5f5 | 301 | 'time' => CRM_Utils_Date::customFormat($toDate), |
cf8f0fff CW |
302 | ], |
303 | ]; | |
202ebbab | 304 | break; |
ea100cb5 | 305 | |
360aaa75 TO |
306 | case 'total clicks on a particular link': |
307 | if (empty($params['target_url'])) { | |
308 | throw new API_Exception("Provide url to get stats result for total clicks on a particular link"); | |
202ebbab | 309 | } |
360aaa75 TO |
310 | // FIXME: doesn't make sense to get url_id mailing_id_(a|b) while getting start date in mailing_id_a |
311 | $url_id = CRM_Mailing_BAO_TrackableURL::getTrackerURLId($mailingAB[$column], $params['target_url']); | |
202ebbab | 312 | $result = CRM_Mailing_Event_BAO_TrackableURLOpen::getRows($mailingAB['mailing_id_a'], NULL, FALSE, $url_id, 0, 1, "civicrm_mailing_event_trackable_url_open.time_stamp ASC"); |
313 | $startDate = CRM_Utils_Date::processDate($result[0]['date']); | |
360aaa75 TO |
314 | $targetDate = CRM_Utils_Date::processDate($params['target_date']); |
315 | $dateDuration = round(abs(strtotime($targetDate) - strtotime($startDate)) / $params['split_count']); | |
202ebbab | 316 | $toDate = strtotime($startDate) + ($dateDuration * $params['split_count_select']); |
317 | $toDate = CRM_Utils_Date::processDate($toDate); | |
cf8f0fff CW |
318 | $graphStats[$name] = [ |
319 | $params['split_count_select'] => [ | |
202ebbab | 320 | 'count' => CRM_Mailing_Event_BAO_TrackableURLOpen::getTotalCount($params['mailing_id'], NULL, FALSE, $url_id, $toDate), |
21dfd5f5 | 321 | 'time' => CRM_Utils_Date::customFormat($toDate), |
cf8f0fff CW |
322 | ], |
323 | ]; | |
202ebbab | 324 | break; |
325 | } | |
326 | } | |
327 | ||
328 | return civicrm_api3_create_success($graphStats); | |
b0f9e1df | 329 | } |