Merge remote-tracking branch 'upstream/4.4' into 4.4-master-2014-06-23-18-25-12
[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 $form->add('checkbox', 'bind_format', ts('Always use this Page Format with the selected Template'));
159 $form->add('checkbox', 'update_format', ts('Update Page Format (this will affect all templates that use this format)'));
160
161 $form->assign('useThisPageFormat', ts('Always use this Page Format with the new template?'));
162 $form->assign('useSelectedPageFormat', ts('Should the new template always use the selected Page Format?'));
163 $form->assign('totalSelectedContacts', count($form->_contactIds));
164
165 CRM_Mailing_BAO_Mailing::commonLetterCompose($form);
166
167 if ($form->_single) {
168 $cancelURL = CRM_Utils_System::url(
169 'civicrm/contact/view',
170 "reset=1&cid={$form->_cid}&selectedChild=activity",
171 FALSE,
172 NULL,
173 FALSE
174 );
175 if ($form->get('action') == CRM_Core_Action::VIEW) {
176 $form->addButtons(array(
177 array(
178 'type' => 'cancel',
179 'name' => ts('Done'),
180 'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"),
181 ),
182 )
183 );
184 }
185 else {
186 $form->addButtons(array(
187 array(
188 'type' => 'submit',
189 'name' => ts('Make PDF Letter'),
190 'isDefault' => TRUE,
191 ),
192 array(
193 'type' => 'cancel',
194 'name' => ts('Done'),
195 'js' => array('onclick' => "location.href='{$cancelURL}'; return false;"),
196 ),
197 )
198 );
199 }
200 }
201 else {
202 $form->addButtons(array(
203 array(
204 'type' => 'submit',
205 'name' => ts('Make PDF Letters'),
206 'isDefault' => TRUE,
207 ),
208 array(
209 'type' => 'cancel',
210 'name' => ts('Done'),
211 ),
212 ));
213 }
214
215 $form->addFormRule(array('CRM_Contact_Form_Task_PDFLetterCommon', 'formRule'), $form);
216 }
217
218 /**
219 * Set default values
220 */
221 static function setDefaultValues() {
222 $defaultFormat = CRM_Core_BAO_PdfFormat::getDefaultValues();
223 $defaultFormat['format_id'] = $defaultFormat['id'];
224 return $defaultFormat;
225 }
226
227 /**
228 * form rule
229 *
230 * @param array $fields the input form values
231 * @param array $dontCare
232 * @param array $self additional values form 'this'
233 *
234 * @return true if no errors, else array of errors
235 * @access public
236 *
237 */
238 static function formRule($fields, $dontCare, $self) {
239 $errors = array();
240 $template = CRM_Core_Smarty::singleton();
241
242 //Added for CRM-1393
243 if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) {
244 $errors['saveTemplateName'] = ts("Enter name to save message template");
245 }
246 if (!is_numeric($fields['margin_left'])) {
247 $errors['margin_left'] = 'Margin must be numeric';
248 }
249 if (!is_numeric($fields['margin_right'])) {
250 $errors['margin_right'] = 'Margin must be numeric';
251 }
252 if (!is_numeric($fields['margin_top'])) {
253 $errors['margin_top'] = 'Margin must be numeric';
254 }
255 if (!is_numeric($fields['margin_bottom'])) {
256 $errors['margin_bottom'] = 'Margin must be numeric';
257 }
258 return empty($errors) ? TRUE : $errors;
259 }
260
261 /**
262 * part of the post process which prepare and extract information from the template
263 *
264 * @access protected
265 *
266 * @param $form
267 *
268 * @return array( $categories, $html_message, $messageToken, $returnProperties )
269 */
270 static protected function processMessageTemplate(&$form) {
271 $formValues = $form->controller->exportValues($form->getName());
272
273 // process message template
274 if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) {
275 $messageTemplate = array(
276 'msg_text' => NULL,
277 'msg_html' => $formValues['html_message'],
278 'msg_subject' => NULL,
279 'is_active' => TRUE,
280 );
281
282 $messageTemplate['pdf_format_id'] = 'null';
283 if (!empty($formValues['bind_format']) && $formValues['format_id']) {
284 $messageTemplate['pdf_format_id'] = $formValues['format_id'];
285 }
286 if (!empty($formValues['saveTemplate']) && $formValues['saveTemplate']) {
287 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
288 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
289 }
290
291 if (!empty($formValues['updateTemplate']) && $formValues['template'] && $formValues['updateTemplate']) {
292 $messageTemplate['id'] = $formValues['template'];
293
294 unset($messageTemplate['msg_title']);
295 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
296 }
297 }
298 elseif (CRM_Utils_Array::value('template', $formValues) > 0) {
299 if (!empty($formValues['bind_format']) && $formValues['format_id']) {
300 $query = "UPDATE civicrm_msg_template SET pdf_format_id = {$formValues['format_id']} WHERE id = {$formValues['template']}";
301 }
302 else {
303 $query = "UPDATE civicrm_msg_template SET pdf_format_id = NULL WHERE id = {$formValues['template']}";
304 }
305 CRM_Core_DAO::executeQuery($query, CRM_Core_DAO::$_nullArray);
306 }
307 if (!empty($formValues['update_format'])) {
308 $bao = new CRM_Core_BAO_PdfFormat();
309 $bao->savePdfFormat($formValues, $formValues['format_id']);
310 }
311
312 $html = array();
313
314 $tokens = array();
315 CRM_Utils_Hook::tokens($tokens);
316 $categories = array_keys($tokens);
317
318 $html_message = $formValues['html_message'];
319
320 //time being hack to strip '&nbsp;'
321 //from particular letter line, CRM-6798
322 self::formatMessage($html_message);
323
324 $messageToken = CRM_Utils_Token::getTokens($html_message);
325
326 $returnProperties = array();
327 if (isset($messageToken['contact'])) {
328 foreach ($messageToken['contact'] as $key => $value) {
329 $returnProperties[$value] = 1;
330 }
331 }
332
333 return array($formValues, $categories, $html_message, $messageToken, $returnProperties);
334 }
335
336 /**
337 * process the form after the input has been submitted and validated
338 *
339 * @access public
340 *
341 * @param $form
342 *
343 * @return void
344 */
345 static function postProcess(&$form) {
346 list($formValues, $categories, $html_message, $messageToken, $returnProperties) = self::processMessageTemplate($form);
347
348 $skipOnHold = isset($form->skipOnHold) ? $form->skipOnHold : FALSE;
349 $skipDeceased = isset($form->skipDeceased) ? $form->skipDeceased : TRUE;
350
351 foreach ($form->_contactIds as $item => $contactId) {
352 $params = array('contact_id' => $contactId);
353
354 list($contact) = CRM_Utils_Token::getTokenDetails($params,
355 $returnProperties,
356 $skipOnHold,
357 $skipDeceased,
358 NULL,
359 $messageToken,
360 'CRM_Contact_Form_Task_PDFLetterCommon'
361 );
362 if (civicrm_error($contact)) {
363 $notSent[] = $contactId;
364 continue;
365 }
366
367 $tokenHtml = CRM_Utils_Token::replaceContactTokens($html_message, $contact[$contactId], TRUE, $messageToken);
368 $tokenHtml = CRM_Utils_Token::replaceHookTokens($tokenHtml, $contact[$contactId], $categories, TRUE);
369
370 if (defined('CIVICRM_MAIL_SMARTY') && CIVICRM_MAIL_SMARTY) {
371 $smarty = CRM_Core_Smarty::singleton();
372 // also add the contact tokens to the template
373 $smarty->assign_by_ref('contact', $contact);
374 $tokenHtml = $smarty->fetch("string:$tokenHtml");
375 }
376
377 $html[] = $tokenHtml;
378 }
379
380 self::createActivities($form, $html_message, $form->_contactIds);
381
382 CRM_Utils_PDF_Utils::html2pdf($html, "CiviLetter.pdf", FALSE, $formValues);
383
384 $form->postProcessHook();
385
386 CRM_Utils_System::civiExit(1);
387 }
388
389 /**
390 * @param $form
391 * @param $html_message
392 * @param $contactIds
393 *
394 * @throws CRM_Core_Exception
395 */
396 static function createActivities($form, $html_message, $contactIds) {
397 //Added for CRM-12682: Add activity subject and campaign fields
398 $formValues = $form->controller->exportValues($form->getName());
399
400 $session = CRM_Core_Session::singleton();
401 $userID = $session->get('userID');
402 $activityTypeID = CRM_Core_OptionGroup::getValue(
403 'activity_type',
404 'Print PDF Letter',
405 'name'
406 );
407 $activityParams = array(
408 'subject' => $formValues['subject'],
409 'campaign_id' => CRM_Utils_Array::value('campaign_id', $formValues),
410 'source_contact_id' => $userID,
411 'activity_type_id' => $activityTypeID,
412 'activity_date_time' => date('YmdHis'),
413 'details' => $html_message,
414 );
415 if (!empty($form->_activityId)) {
416 $activityParams += array('id' => $form->_activityId);
417 }
418 if ($form->_cid) {
419 $activity = CRM_Activity_BAO_Activity::create($activityParams);
420 }
421 else {
422 // create Print PDF activity for each selected contact. CRM-6886
423 $activityIds = array();
424 foreach ($contactIds as $contactId) {
425 $activityID = CRM_Activity_BAO_Activity::create($activityParams);
426 $activityIds[$contactId] = $activityID->id;
427 }
428 }
429
430 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
431 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
432
433 //@todo why are we using $form->_contactIds here & contactIds above - need comment
434 foreach ($form->_contactIds as $contactId) {
435 $activityTargetParams = array(
436 'activity_id' => empty($activity->id) ? $activityIds[$contactId] : $activity->id,
437 'contact_id' => $contactId,
438 'record_type_id' => $targetID
439 );
440 CRM_Activity_BAO_ActivityContact::create($activityTargetParams);
441 }
442 }
443
444 /**
445 * @param $message
446 */
447 static function formatMessage(&$message) {
448 $newLineOperators = array(
449 'p' => array(
450 'oper' => '<p>',
451 'pattern' => '/<(\s+)?p(\s+)?>/m',
452 ),
453 'br' => array(
454 'oper' => '<br />',
455 'pattern' => '/<(\s+)?br(\s+)?\/>/m',
456 ),
457 );
458
459 $htmlMsg = preg_split($newLineOperators['p']['pattern'], $message);
460 foreach ($htmlMsg as $k => & $m) {
461 $messages = preg_split($newLineOperators['br']['pattern'], $m);
462 foreach ($messages as $key => & $msg) {
463 $msg = trim($msg);
464 $matches = array();
465 if (preg_match('/^(&nbsp;)+/', $msg, $matches)) {
466 $spaceLen = strlen($matches[0]) / 6;
467 $trimMsg = ltrim($msg, '&nbsp; ');
468 $charLen = strlen($trimMsg);
469 $totalLen = $charLen + $spaceLen;
470 if ($totalLen > 100) {
471 $spacesCount = 10;
472 if ($spaceLen > 50) {
473 $spacesCount = 20;
474 }
475 if ($charLen > 100) {
476 $spacesCount = 1;
477 }
478 $msg = str_repeat('&nbsp;', $spacesCount) . $trimMsg;
479 }
480 }
481 }
482 $m = implode($newLineOperators['br']['oper'], $messages);
483 }
484 $message = implode($newLineOperators['p']['oper'], $htmlMsg);
485 }
486 }
487