Merge pull request #4882 from colemanw/CRM-15789
[civicrm-core.git] / CRM / Core / BAO / MessageTemplate.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35
36 require_once 'Mail/mime.php';
37
38 /**
39 * Class CRM_Core_BAO_MessageTemplate
40 */
41 class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate {
42
43 /**
44 * Fetch object based on array of properties
45 *
46 * @param array $params
47 * (reference ) an assoc array of name/value pairs.
48 * @param array $defaults
49 * (reference ) an assoc array to hold the flattened values.
50 *
51 * @return CRM_Core_BAO_MessageTemplate object
52 * @static
53 */
54 public static function retrieve(&$params, &$defaults) {
55 $messageTemplates = new CRM_Core_DAO_MessageTemplate();
56 $messageTemplates->copyValues($params);
57 if ($messageTemplates->find(TRUE)) {
58 CRM_Core_DAO::storeValues($messageTemplates, $defaults);
59 return $messageTemplates;
60 }
61 return NULL;
62 }
63
64 /**
65 * Update the is_active flag in the db
66 *
67 * @param int $id
68 * Id of the database record.
69 * @param bool $is_active
70 * Value we want to set the is_active field.
71 *
72 * @return Object DAO object on sucess, NULL otherwise
73 * @static
74 */
75 public static function setIsActive($id, $is_active) {
76 return CRM_Core_DAO::setFieldValue('CRM_Core_DAO_MessageTemplate', $id, 'is_active', $is_active);
77 }
78
79 /**
80 * Add the Message Templates
81 *
82 * @param array $params
83 * Reference array contains the values submitted by the form.
84 *
85 * @static
86 *
87 * @return object
88 */
89 public static function add(&$params) {
90 $hook = empty($params['id']) ? 'create' : 'edit';
91 CRM_Utils_Hook::pre($hook, 'MessageTemplate', CRM_Utils_Array::value('id', $params), $params);
92
93 $messageTemplates = new CRM_Core_DAO_MessageTemplate();
94 $messageTemplates->copyValues($params);
95 $messageTemplates->save();
96
97 CRM_Utils_Hook::post($hook, 'MessageTemplate', $messageTemplates->id, $messageTemplates);
98 return $messageTemplates;
99 }
100
101 /**
102 * Delete the Message Templates
103 *
104 * @static
105 *
106 * @param int $messageTemplatesID
107 *
108 * @return object
109 */
110 public static function del($messageTemplatesID) {
111 // make sure messageTemplatesID is an integer
112 if (!CRM_Utils_Rule::positiveInteger($messageTemplatesID)) {
113 CRM_Core_Error::fatal(ts('Invalid Message template'));
114 }
115
116 // Set mailing msg template col to NULL
117 $query = "UPDATE civicrm_mailing
118 SET msg_template_id = NULL
119 WHERE msg_template_id = %1";
120
121 $params = array(1 => array($messageTemplatesID, 'Integer'));
122 CRM_Core_DAO::executeQuery($query, $params);
123
124 $messageTemplates = new CRM_Core_DAO_MessageTemplate();
125 $messageTemplates->id = $messageTemplatesID;
126 $messageTemplates->delete();
127 CRM_Core_Session::setStatus(ts('Selected message template has been deleted.'), ts('Deleted'), 'success');
128 }
129
130 /**
131 * Get the Message Templates
132 *
133 * @static
134 *
135 * @param bool $all
136 *
137 * @return object
138 */
139 public static function getMessageTemplates($all = TRUE, $isSMS = FALSE) {
140 $msgTpls = array();
141
142 $messageTemplates = new CRM_Core_DAO_MessageTemplate();
143 $messageTemplates->is_active = 1;
144 $messageTemplates->is_sms = $isSMS;
145
146 if (!$all) {
147 $messageTemplates->workflow_id = 'NULL';
148 }
149 $messageTemplates->find();
150 while ($messageTemplates->fetch()) {
151 $msgTpls[$messageTemplates->id] = $messageTemplates->msg_title;
152 }
153 asort($msgTpls);
154 return $msgTpls;
155 }
156
157 /**
158 * @param int $contactId
159 * @param $email
160 * @param int $messageTemplateID
161 * @param $from
162 *
163 * @return bool|NULL
164 */
165 public static function sendReminder($contactId, $email, $messageTemplateID, $from) {
166
167 $messageTemplates = new CRM_Core_DAO_MessageTemplate();
168 $messageTemplates->id = $messageTemplateID;
169
170 $domain = CRM_Core_BAO_Domain::getDomain();
171 $result = NULL;
172 $hookTokens = array();
173
174 if ($messageTemplates->find(TRUE)) {
175 $body_text = $messageTemplates->msg_text;
176 $body_html = $messageTemplates->msg_html;
177 $body_subject = $messageTemplates->msg_subject;
178 if (!$body_text) {
179 $body_text = CRM_Utils_String::htmlToText($body_html);
180 }
181
182 $params = array(array('contact_id', '=', $contactId, 0, 0));
183 list($contact, $_) = CRM_Contact_BAO_Query::apiQuery($params);
184
185 //CRM-4524
186 $contact = reset($contact);
187
188 if (!$contact || is_a($contact, 'CRM_Core_Error')) {
189 return NULL;
190 }
191
192 //CRM-5734
193
194 // get tokens to be replaced
195 $tokens = array_merge(CRM_Utils_Token::getTokens($body_text),
196 CRM_Utils_Token::getTokens($body_html),
197 CRM_Utils_Token::getTokens($body_subject));
198
199 // get replacement text for these tokens
200 $returnProperties = array("preferred_mail_format" => 1);
201 if (isset($tokens['contact'])) {
202 foreach ($tokens['contact'] as $key => $value) {
203 $returnProperties[$value] = 1;
204 }
205 }
206 list($details) = CRM_Utils_Token::getTokenDetails(array($contactId),
207 $returnProperties,
208 NULL, NULL, FALSE,
209 $tokens,
210 'CRM_Core_BAO_MessageTemplate');
211 $contact = reset($details);
212
213 // call token hook
214 $hookTokens = array();
215 CRM_Utils_Hook::tokens($hookTokens);
216 $categories = array_keys($hookTokens);
217
218 // do replacements in text and html body
219 $type = array('html', 'text');
220 foreach ($type as $key => $value) {
221 $bodyType = "body_{$value}";
222 if ($$bodyType) {
223 CRM_Utils_Token::replaceGreetingTokens($$bodyType, NULL, $contact['contact_id']);
224 $$bodyType = CRM_Utils_Token::replaceDomainTokens($$bodyType, $domain, TRUE, $tokens, TRUE);
225 $$bodyType = CRM_Utils_Token::replaceContactTokens($$bodyType, $contact, FALSE, $tokens, FALSE, TRUE);
226 $$bodyType = CRM_Utils_Token::replaceComponentTokens($$bodyType, $contact, $tokens, TRUE);
227 $$bodyType = CRM_Utils_Token::replaceHookTokens($$bodyType, $contact, $categories, TRUE);
228 }
229 }
230 $html = $body_html;
231 $text = $body_text;
232
233 $smarty = CRM_Core_Smarty::singleton();
234 foreach (array(
235 'text', 'html') as $elem) {
236 $$elem = $smarty->fetch("string:{$$elem}");
237 }
238
239 // do replacements in message subject
240 $messageSubject = CRM_Utils_Token::replaceContactTokens($body_subject, $contact, FALSE, $tokens);
241 $messageSubject = CRM_Utils_Token::replaceDomainTokens($messageSubject, $domain, TRUE, $tokens);
242 $messageSubject = CRM_Utils_Token::replaceComponentTokens($messageSubject, $contact, $tokens, TRUE);
243 $messageSubject = CRM_Utils_Token::replaceHookTokens($messageSubject, $contact, $categories, TRUE);
244
245 $messageSubject = $smarty->fetch("string:{$messageSubject}");
246
247 // set up the parameters for CRM_Utils_Mail::send
248 $mailParams = array(
249 'groupName' => 'Scheduled Reminder Sender',
250 'from' => $from,
251 'toName' => $contact['display_name'],
252 'toEmail' => $email,
253 'subject' => $messageSubject,
254 );
255 if (!$html || $contact['preferred_mail_format'] == 'Text' ||
256 $contact['preferred_mail_format'] == 'Both'
257 ) {
258 // render the &amp; entities in text mode, so that the links work
259 $mailParams['text'] = str_replace('&amp;', '&', $text);
260 }
261 if ($html && ($contact['preferred_mail_format'] == 'HTML' ||
262 $contact['preferred_mail_format'] == 'Both'
263 )) {
264 $mailParams['html'] = $html;
265 }
266
267 $result = CRM_Utils_Mail::send($mailParams);
268 }
269
270 $messageTemplates->free();
271
272 return $result;
273 }
274
275 /**
276 * Revert a message template to its default subject+text+HTML state
277 *
278 * @param int id id of the template
279 *
280 * @return void
281 */
282 public static function revert($id) {
283 $diverted = new self;
284 $diverted->id = (int) $id;
285 $diverted->find(1);
286
287 if ($diverted->N != 1) {
288 CRM_Core_Error::fatal(ts('Did not find a message template with id of %1.', array(1 => $id)));
289 }
290
291 $orig = new self;
292 $orig->workflow_id = $diverted->workflow_id;
293 $orig->is_reserved = 1;
294 $orig->find(1);
295
296 if ($orig->N != 1) {
297 CRM_Core_Error::fatal(ts('Message template with id of %1 does not have a default to revert to.', array(1 => $id)));
298 }
299
300 $diverted->msg_subject = $orig->msg_subject;
301 $diverted->msg_text = $orig->msg_text;
302 $diverted->msg_html = $orig->msg_html;
303 $diverted->pdf_format_id = is_null($orig->pdf_format_id) ? 'null' : $orig->pdf_format_id;
304 $diverted->save();
305 }
306
307 /**
308 * Send an email from the specified template based on an array of params
309 *
310 * @param array $params
311 * A string-keyed array of function params, see function body for details.
312 *
313 * @return array of four parameters: a boolean whether the email was sent, and the subject, text and HTML templates
314 */
315 public static function sendTemplate($params) {
316 $defaults = array(
317 // option group name of the template
318 'groupName' => NULL,
319 // option value name of the template
320 'valueName' => NULL,
321 // ID of the template
322 'messageTemplateID' => NULL,
323 // contact id if contact tokens are to be replaced
324 'contactId' => NULL,
325 // additional template params (other than the ones already set in the template singleton)
326 'tplParams' => array(),
327 // the From: header
328 'from' => NULL,
329 // the recipient’s name
330 'toName' => NULL,
331 // the recipient’s email - mail is sent only if set
332 'toEmail' => NULL,
333 // the Cc: header
334 'cc' => NULL,
335 // the Bcc: header
336 'bcc' => NULL,
337 // the Reply-To: header
338 'replyTo' => NULL,
339 // email attachments
340 'attachments' => NULL,
341 // whether this is a test email (and hence should include the test banner)
342 'isTest' => FALSE,
343 // filename of optional PDF version to add as attachment (do not include path)
344 'PDFFilename' => NULL,
345 );
346 $params = array_merge($defaults, $params);
347
348 if ((!$params['groupName'] ||
349 !$params['valueName']
350 ) &&
351 !$params['messageTemplateID']
352 ) {
353 CRM_Core_Error::fatal(ts("Message template's option group and/or option value or ID missing."));
354 }
355
356 if ($params['messageTemplateID']) {
357 // fetch the three elements from the db based on id
358 $query = 'SELECT msg_subject subject, msg_text text, msg_html html, pdf_format_id format
359 FROM civicrm_msg_template mt
360 WHERE mt.id = %1 AND mt.is_default = 1';
361 $sqlParams = array(1 => array($params['messageTemplateID'], 'String'));
362 }
363 else {
364 // fetch the three elements from the db based on option_group and option_value names
365 $query = 'SELECT msg_subject subject, msg_text text, msg_html html, pdf_format_id format
366 FROM civicrm_msg_template mt
367 JOIN civicrm_option_value ov ON workflow_id = ov.id
368 JOIN civicrm_option_group og ON ov.option_group_id = og.id
369 WHERE og.name = %1 AND ov.name = %2 AND mt.is_default = 1';
370 $sqlParams = array(1 => array($params['groupName'], 'String'), 2 => array($params['valueName'], 'String'));
371 }
372 $dao = CRM_Core_DAO::executeQuery($query, $sqlParams);
373 $dao->fetch();
374
375 if (!$dao->N) {
376 if ($params['messageTemplateID']) {
377 CRM_Core_Error::fatal(ts('No such message template: id=%1.', array(1 => $params['messageTemplateID'])));
378 }
379 else {
380 CRM_Core_Error::fatal(ts('No such message template: option group %1, option value %2.', array(1 => $params['groupName'], 2 => $params['valueName'])));
381 }
382 }
383
384 $subject = $dao->subject;
385 $text = $dao->text;
386 $html = $dao->html;
387 $format = $dao->format;
388 $dao->free();
389
390 // add the test banner (if requested)
391 if ($params['isTest']) {
392 $query = "SELECT msg_subject subject, msg_text text, msg_html html
393 FROM civicrm_msg_template mt
394 JOIN civicrm_option_value ov ON workflow_id = ov.id
395 JOIN civicrm_option_group og ON ov.option_group_id = og.id
396 WHERE og.name = 'msg_tpl_workflow_meta' AND ov.name = 'test_preview' AND mt.is_default = 1";
397 $testDao = CRM_Core_DAO::executeQuery($query);
398 $testDao->fetch();
399
400 $subject = $testDao->subject . $subject;
401 $text = $testDao->text . $text;
402 $html = preg_replace('/<body(.*)$/im', "<body\\1\n{$testDao->html}", $html);
403 $testDao->free();
404 }
405
406 // replace tokens in the three elements (in subject as if it was the text body)
407 $domain = CRM_Core_BAO_Domain::getDomain();
408 $hookTokens = array();
409 $mailing = new CRM_Mailing_BAO_Mailing;
410 $mailing->body_text = $text;
411 $mailing->body_html = $html;
412 $tokens = $mailing->getTokens();
413 CRM_Utils_Hook::tokens($hookTokens);
414 $categories = array_keys($hookTokens);
415
416 $contactID = CRM_Utils_Array::value('contactId', $params);
417
418 if ($contactID) {
419 $contactParams = array('contact_id' => $contactID);
420 $returnProperties = array();
421
422 if (isset($tokens['text']['contact'])) {
423 foreach ($tokens['text']['contact'] as $name) {
424 $returnProperties[$name] = 1;
425 }
426 }
427
428 if (isset($tokens['html']['contact'])) {
429 foreach ($tokens['html']['contact'] as $name) {
430 $returnProperties[$name] = 1;
431 }
432 }
433 list($contact) = CRM_Utils_Token::getTokenDetails($contactParams,
434 $returnProperties,
435 FALSE, FALSE, NULL,
436 CRM_Utils_Token::flattenTokens($tokens),
437 // we should consider adding groupName and valueName here
438 'CRM_Core_BAO_MessageTemplate'
439 );
440 $contact = $contact[$contactID];
441 }
442
443 $subject = CRM_Utils_Token::replaceDomainTokens($subject, $domain, FALSE, $tokens['text'], TRUE);
444 $text = CRM_Utils_Token::replaceDomainTokens($text, $domain, FALSE, $tokens['text'], TRUE);
445 $html = CRM_Utils_Token::replaceDomainTokens($html, $domain, TRUE, $tokens['html'], TRUE);
446
447 if ($contactID) {
448 $subject = CRM_Utils_Token::replaceContactTokens($subject, $contact, FALSE, $tokens['text'], FALSE, TRUE);
449 $text = CRM_Utils_Token::replaceContactTokens($text, $contact, FALSE, $tokens['text'], FALSE, TRUE);
450 $html = CRM_Utils_Token::replaceContactTokens($html, $contact, FALSE, $tokens['html'], FALSE, TRUE);
451
452
453 $contactArray = array($contactID => $contact);
454 CRM_Utils_Hook::tokenValues($contactArray,
455 array($contactID),
456 NULL,
457 CRM_Utils_Token::flattenTokens($tokens),
458 // we should consider adding groupName and valueName here
459 'CRM_Core_BAO_MessageTemplate'
460 );
461 $contact = $contactArray[$contactID];
462
463 $subject = CRM_Utils_Token::replaceHookTokens($subject, $contact, $categories, TRUE);
464 $text = CRM_Utils_Token::replaceHookTokens($text, $contact, $categories, TRUE);
465 $html = CRM_Utils_Token::replaceHookTokens($html, $contact, $categories, TRUE);
466 }
467
468 // strip whitespace from ends and turn into a single line
469 $subject = "{strip}$subject{/strip}";
470
471 // parse the three elements with Smarty
472
473
474 $smarty = CRM_Core_Smarty::singleton();
475 foreach ($params['tplParams'] as $name => $value) {
476 $smarty->assign($name, $value);
477 }
478 foreach (array(
479 'subject', 'text', 'html') as $elem) {
480 $$elem = $smarty->fetch("string:{$$elem}");
481 }
482
483 // send the template, honouring the target user’s preferences (if any)
484 $sent = FALSE;
485
486 // create the params array
487 $params['subject'] = $subject;
488 $params['text'] = $text;
489 $params['html'] = $html;
490
491 if ($params['toEmail']) {
492 $contactParams = array(array('email', 'LIKE', $params['toEmail'], 0, 1));
493 list($contact, $_) = CRM_Contact_BAO_Query::apiQuery($contactParams);
494
495 $prefs = array_pop($contact);
496
497 if (isset($prefs['preferred_mail_format']) and $prefs['preferred_mail_format'] == 'HTML') {
498 $params['text'] = NULL;
499 }
500
501 if (isset($prefs['preferred_mail_format']) and $prefs['preferred_mail_format'] == 'Text') {
502 $params['html'] = NULL;
503 }
504
505 $config = CRM_Core_Config::singleton();
506 if (isset($params['isEmailPdf']) && $params['isEmailPdf'] == 1) {
507 $pdfHtml = CRM_Contribute_BAO_ContributionPage::addInvoicePdfToEmail($params['contributionId'], $params['contactId']);
508 if (empty($params['attachments'])) {
509 $params['attachments'] = array();
510 }
511 $params['attachments'][] = CRM_Utils_Mail::appendPDF('Invoice.pdf', $pdfHtml, $format);
512 }
513 $pdf_filename = '';
514 if ($config->doNotAttachPDFReceipt &&
515 $params['PDFFilename'] &&
516 $params['html']
517 ) {
518 if (empty($params['attachments'])) {
519 $params['attachments'] = array();
520 }
521 $params['attachments'][] = CRM_Utils_Mail::appendPDF($params['PDFFilename'], $params['html'], $format);
522 if (isset($params['tplParams']['email_comment'])) {
523 $params['html'] = $params['tplParams']['email_comment'];
524 $params['text'] = strip_tags($params['tplParams']['email_comment']);
525 }
526 }
527
528 $sent = CRM_Utils_Mail::send($params);
529
530 if ($pdf_filename) {
531 unlink($pdf_filename);
532 }
533 }
534
535 return array($sent, $subject, $text, $html);
536 }
537 }