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