Merge remote-tracking branch 'origin/4.4' into 4.4-4.5-2014-09-14-13-58-42
[civicrm-core.git] / CRM / Contact / Form / Task / PDFLetterCommon.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
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 /**
37 * This class provides the common functionality for creating PDF letter for
38 * one or a group of contact ids.
39 */
40 class CRM_Contact_Form_Task_PDFLetterCommon {
41
42 /**
43 * build all the data structures needed to build the form
44 *
45 * @param $form
46 *
47 * @return void
48 * @access public
49 */
50 static function preProcess(&$form) {
51 $messageText = array();
52 $messageSubject = array();
53 $dao = new CRM_Core_BAO_MessageTemplate();
54 $dao->is_active = 1;
55 $dao->find();
56 while ($dao->fetch()) {
57 $messageText[$dao->id] = $dao->msg_text;
58 $messageSubject[$dao->id] = $dao->msg_subject;
59 }
60
61 $form->assign('message', $messageText);
62 $form->assign('messageSubject', $messageSubject);
63 CRM_Utils_System::setTitle('Create Printable Letters (PDF)');
64 }
65
66 /**
67 * @param $form
68 * @param $cid
69 */
70 static function preProcessSingle(&$form, $cid) {
71 $form->_contactIds = array($cid);
72 // put contact display name in title for single contact mode
73 CRM_Utils_System::setTitle(ts('Create Printable Letter (PDF) for %1', array(1 => CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'display_name'))));
74 }
75
76 /**
77 * Build the form
78 *
79 * @var CRM_Core_Form $form
80 *
81 * @return void
82 */
83 static function buildQuickForm(&$form) {
84 // This form outputs a file so should never be submitted via ajax
85 $form->preventAjaxSubmit();
86
87 //Added for CRM-12682: Add activity subject and campaign fields
88 CRM_Campaign_BAO_Campaign::addCampaign($form);
89 $form->add(
90 'text',
91 'subject',
92 ts('Activity Subject'),
93 array('size' => 45, 'maxlength' => 255),
94 FALSE
95 );
96
97 $form->add('static', 'pdf_format_header', NULL, ts('Page Format: %1', array(1 => '<span class="pdf-format-header-label"></span>')));
98 $form->addSelect('format_id', array(
99 'label' => ts('Select Format'),
100 'placeholder' => ts('Default'),
101 'entity' => 'message_template',
102 'field' => 'pdf_format_id',
103 'option_url' => 'civicrm/admin/pdfFormats',
104 ));
105 $form->add(
106 'select',
107 'paper_size',
108 ts('Paper Size'),
109 array(0 => ts('- default -')) + CRM_Core_BAO_PaperSize::getList(TRUE),
110 FALSE,
111 array('onChange' => "selectPaper( this.value ); showUpdateFormatChkBox();")
112 );
113 $form->add('static', 'paper_dimensions', NULL, ts('Width x Height'));
114 $form->add(
115 'select',
116 'orientation',
117 ts('Orientation'),
118 CRM_Core_BAO_PdfFormat::getPageOrientations(),
119 FALSE,
120 array('onChange' => "updatePaperDimensions(); showUpdateFormatChkBox();")
121 );
122 $form->add(
123 'select',
124 'metric',
125 ts('Unit of Measure'),
126 CRM_Core_BAO_PdfFormat::getUnits(),
127 FALSE,
128 array('onChange' => "selectMetric( this.value );")
129 );
130 $form->add(
131 'text',
132 'margin_left',
133 ts('Left Margin'),
134 array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"),
135 TRUE
136 );
137 $form->add(
138 'text',
139 'margin_right',
140 ts('Right Margin'),
141 array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"),
142 TRUE
143 );
144 $form->add(
145 'text',
146 'margin_top',
147 ts('Top Margin'),
148 array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"),
149 TRUE
150 );
151 $form->add(
152 'text',
153 'margin_bottom',
154 ts('Bottom Margin'),
155 array('size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"),
156 TRUE
157 );
158
159 $config = CRM_Core_Config::singleton();
160 if ($config->wkhtmltopdfPath == false) {
161 $form->add(
162 'text',
163 'stationery',
164 ts('Stationery (relative path to PDF you wish to use as the background)'),
165 array('size' => 25, 'maxlength' => 900, 'onkeyup' => "showUpdateFormatChkBox();"),
166 FALSE
167 );
168 }
169 $form->add('checkbox', 'bind_format', ts('Always use this Page Format with the selected Template'));
170 $form->add('checkbox', 'update_format', ts('Update Page Format (this will affect all templates that use this format)'));
171
172 $form->assign('useThisPageFormat', ts('Always use this Page Format with the new template?'));
173 $form->assign('useSelectedPageFormat', ts('Should the new template always use the selected Page Format?'));
174 $form->assign('totalSelectedContacts', count($form->_contactIds));
175
176 CRM_Mailing_BAO_Mailing::commonLetterCompose($form);
177
178 if ($form->_single) {
179 $cancelURL = CRM_Utils_System::url(
180 'civicrm/contact/view',
181 "reset=1&cid={$form->_cid}&selectedChild=activity",
182 FALSE,
183 NULL,
184 FALSE
185 );
186
187 if ($form->get('action') == CRM_Core_Action::VIEW) {
188 $form->addButtons(array(
189 array(
190 'type' => 'cancel',
191 'name' => ts('Done'),
192 'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"),
193 ),
194 )
195 );
196 }
197 else {
198 $form->addButtons(array(
199 array(
200 'type' => 'submit',
201 'name' => ts('Make PDF Letter'),
202 'isDefault' => TRUE,
203 ),
204 array(
205 'type' => 'cancel',
206 'name' => ts('Done'),
207 'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"),
208 ),
209 )
210 );
211 }
212 }
213 else {
214 $form->addButtons(array(
215 array(
216 'type' => 'submit',
217 'name' => ts('Make PDF Letters'),
218 'isDefault' => TRUE,
219 ),
220 array(
221 'type' => 'cancel',
222 'name' => ts('Done'),
223 ),
224 ));
225 }
226
227 $form->addFormRule(array('CRM_Contact_Form_Task_PDFLetterCommon', 'formRule'), $form);
228 }
229
230 /**
231 * Set default values
232 */
233 static function setDefaultValues() {
234 $defaultFormat = CRM_Core_BAO_PdfFormat::getDefaultValues();
235 $defaultFormat['format_id'] = $defaultFormat['id'];
236 return $defaultFormat;
237 }
238
239 /**
240 * form rule
241 *
242 * @param array $fields the input form values
243 * @param array $dontCare
244 * @param array $self additional values form 'this'
245 *
246 * @return true if no errors, else array of errors
247 * @access public
248 *
249 */
250 static function formRule($fields, $dontCare, $self) {
251 $errors = array();
252 $template = CRM_Core_Smarty::singleton();
253
254 //Added for CRM-1393
255 if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) {
256 $errors['saveTemplateName'] = ts("Enter name to save message template");
257 }
258 if (!is_numeric($fields['margin_left'])) {
259 $errors['margin_left'] = 'Margin must be numeric';
260 }
261 if (!is_numeric($fields['margin_right'])) {
262 $errors['margin_right'] = 'Margin must be numeric';
263 }
264 if (!is_numeric($fields['margin_top'])) {
265 $errors['margin_top'] = 'Margin must be numeric';
266 }
267 if (!is_numeric($fields['margin_bottom'])) {
268 $errors['margin_bottom'] = 'Margin must be numeric';
269 }
270 return empty($errors) ? TRUE : $errors;
271 }
272
273 /**
274 * part of the post process which prepare and extract information from the template
275 *
276 * @access protected
277 *
278 * @param $form
279 *
280 * @return array( $categories, $html_message, $messageToken, $returnProperties )
281 */
282 static protected function processMessageTemplate(&$form) {
283 $formValues = $form->controller->exportValues($form->getName());
284
285 // process message template
286 if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) {
287 $messageTemplate = array(
288 'msg_text' => NULL,
289 'msg_html' => $formValues['html_message'],
290 'msg_subject' => NULL,
291 'is_active' => TRUE,
292 );
293
294 $messageTemplate['pdf_format_id'] = 'null';
295 if (!empty($formValues['bind_format']) && $formValues['format_id']) {
296 $messageTemplate['pdf_format_id'] = $formValues['format_id'];
297 }
298 if (!empty($formValues['saveTemplate']) && $formValues['saveTemplate']) {
299 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
300 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
301 }
302
303 if (!empty($formValues['updateTemplate']) && $formValues['template'] && $formValues['updateTemplate']) {
304 $messageTemplate['id'] = $formValues['template'];
305
306 unset($messageTemplate['msg_title']);
307 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
308 }
309 }
310 elseif (CRM_Utils_Array::value('template', $formValues) > 0) {
311 if (!empty($formValues['bind_format']) && $formValues['format_id']) {
312 $query = "UPDATE civicrm_msg_template SET pdf_format_id = {$formValues['format_id']} WHERE id = {$formValues['template']}";
313 }
314 else {
315 $query = "UPDATE civicrm_msg_template SET pdf_format_id = NULL WHERE id = {$formValues['template']}";
316 }
317 CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
318 }
319 if (!empty($formValues['update_format'])) {
320 $bao = new CRM_Core_BAO_PdfFormat();
321 $bao->savePdfFormat($formValues, $formValues['format_id']);
322 }
323
324 $html = array();
325
326 $tokens = array();
327 CRM_Utils_Hook::tokens($tokens);
328 $categories = array_keys($tokens);
329
330 $html_message = $formValues['html_message'];
331
332 //time being hack to strip '&nbsp;'
333 //from particular letter line, CRM-6798
334 self::formatMessage($html_message);
335
336 $messageToken = CRM_Utils_Token::getTokens($html_message);
337
338 $returnProperties = array();
339 if (isset($messageToken['contact'])) {
340 foreach ($messageToken['contact'] as $key => $value) {
341 $returnProperties[$value] = 1;
342 }
343 }
344
345 return array($formValues, $categories, $html_message, $messageToken, $returnProperties);
346 }
347
348 /**
349 * process the form after the input has been submitted and validated
350 *
351 * @access public
352 *
353 * @param $form
354 *
355 * @return void
356 */
357 static function postProcess(&$form) {
358 list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self::processMessageTemplate($form);
359
360 $skipOnHold = isset($form->skipOnHold) ? $form->skipOnHold : FALSE;
361 $skipDeceased = isset($form->skipDeceased) ? $form->skipDeceased : TRUE;
362
363 foreach ($form->_contactIds as $item => $contactId) {
364 $params = array('contact_id' => $contactId);
365
366 list($contact) = CRM_Utils_Token::getTokenDetails($params,
367 $returnProperties,
368 $skipOnHold,
369 $skipDeceased,
370 NULL,
371 $messageToken,
372 'CRM_Contact_Form_Task_PDFLetterCommon'
373 );
374 if (civicrm_error($contact)) {
375 $notSent[] = $contactId;
376 continue;
377 }
378
379 $tokenHtml = CRM_Utils_Token::replaceContactTokens($html_message, $contact[$contactId], TRUE, $messageToken);
380 $tokenHtml = CRM_Utils_Token::replaceHookTokens($tokenHtml, $contact[$contactId], $categories, TRUE);
381
382 if (defined('CIVICRM_MAIL_SMARTY') && CIVICRM_MAIL_SMARTY) {
383 $smarty = CRM_Core_Smarty::singleton();
384 // also add the contact tokens to the template
385 $smarty->assign_by_ref('contact', $contact);
386 $tokenHtml = $smarty->fetch("string:$tokenHtml");
387 }
388
389 $html[] = $tokenHtml;
390 }
391
392 self::createActivities($form, $html_message, $form->_contactIds);
393
394 CRM_Utils_PDF_Utils::html2pdf($html, "CiviLetter.pdf", FALSE, $formValues);
395
396 $form->postProcessHook();
397
398 CRM_Utils_System::civiExit(1);
399 }
400
401 /**
402 * @param $form
403 * @param $html_message
404 * @param $contactIds
405 *
406 * @throws CRM_Core_Exception
407 */
408 static function createActivities($form, $html_message, $contactIds) {
409 //Added for CRM-12682: Add activity subject and campaign fields
410 $formValues = $form->controller->exportValues($form->getName());
411
412 $session = CRM_Core_Session::singleton();
413 $userID = $session->get('userID');
414 $activityTypeID = CRM_Core_OptionGroup::getValue(
415 'activity_type',
416 'Print PDF Letter',
417 'name'
418 );
419 $activityParams = array(
420 'subject' => $formValues['subject'],
421 'campaign_id' => CRM_Utils_Array::value('campaign_id', $formValues),
422 'source_contact_id' => $userID,
423 'activity_type_id' => $activityTypeID,
424 'activity_date_time' => date('YmdHis'),
425 'details' => $html_message,
426 );
427 if (!empty($form->_activityId)) {
428 $activityParams += array('id' => $form->_activityId);
429 }
430 if ($form->_cid) {
431 $activity = CRM_Activity_BAO_Activity::create($activityParams);
432 }
433 else {
434 // create Print PDF activity for each selected contact. CRM-6886
435 $activityIds = array();
436 foreach ($contactIds as $contactId) {
437 $activityID = CRM_Activity_BAO_Activity::create($activityParams);
438 $activityIds[$contactId] = $activityID->id;
439 }
440 }
441
442 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
443 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
444
445 //@todo why are we using $form->_contactIds here & contactIds above - need comment
446 foreach ($form->_contactIds as $contactId) {
447 $activityTargetParams = array(
448 'activity_id' => empty($activity->id) ? $activityIds[$contactId] : $activity->id,
449 'contact_id' => $contactId,
450 'record_type_id' => $targetID
451 );
452 CRM_Activity_BAO_ActivityContact::create($activityTargetParams);
453 }
454 }
455
456 /**
457 * @param $message
458 */
459 static function formatMessage(&$message) {
460 $newLineOperators = array(
461 'p' => array(
462 'oper' => '<p>',
463 'pattern' => '/<(\s+)?p(\s+)?>/m',
464 ),
465 'br' => array(
466 'oper' => '<br />',
467 'pattern' => '/<(\s+)?br(\s+)?\/>/m',
468 ),
469 );
470
471 $htmlMsg = preg_split($newLineOperators['p']['pattern'], $message);
472 foreach ($htmlMsg as $k => & $m) {
473 $messages = preg_split($newLineOperators['br']['pattern'], $m);
474 foreach ($messages as $key => & $msg) {
475 $msg = trim($msg);
476 $matches = array();
477 if (preg_match('/^(&nbsp;)+/', $msg, $matches)) {
478 $spaceLen = strlen($matches[0]) / 6;
479 $trimMsg = ltrim($msg, '&nbsp; ');
480 $charLen = strlen($trimMsg);
481 $totalLen = $charLen + $spaceLen;
482 if ($totalLen > 100) {
483 $spacesCount = 10;
484 if ($spaceLen > 50) {
485 $spacesCount = 20;
486 }
487 if ($charLen > 100) {
488 $spacesCount = 1;
489 }
490 $msg = str_repeat('&nbsp;', $spacesCount) . $trimMsg;
491 }
492 }
493 }
494 $m = implode($newLineOperators['br']['oper'], $messages);
495 }
496 $message = implode($newLineOperators['p']['oper'], $htmlMsg);
497 }
498 }
499