Merge pull request #18717 from eileenmcnaughton/static2
[civicrm-core.git] / CRM / Core / Form / Task / PDFLetterCommon.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * This is the base class for common PDF/Doc Merge functionality.
20 * Most CRM_*_Form_Task_PDFLetterCommon classes extend the Contact version
21 * but the assumptions there are not always appropriate for other classes
22 * resulting in code duplication and unexpected dependencies.
23 * The intention is that common functionality can be moved here and the other
24 * classes cleaned up.
25 * Keep old-style token handling out of this class.
26 */
27 class CRM_Core_Form_Task_PDFLetterCommon {
28
29 /**
30 * @var CRM_Core_Form $form
31 */
32 public static function preProcess(&$form) {
33 CRM_Utils_System::setTitle('Print/Merge Document');
34 }
35
36 /**
37 * Build the form object.
38 *
39 * @var CRM_Core_Form $form
40 */
41 public static function buildQuickForm(&$form) {
42 // This form outputs a file so should never be submitted via ajax
43 $form->preventAjaxSubmit();
44
45 //Added for CRM-12682: Add activity subject and campaign fields
46 CRM_Campaign_BAO_Campaign::addCampaign($form);
47 $form->add(
48 'text',
49 'subject',
50 ts('Activity Subject'),
51 ['size' => 45, 'maxlength' => 255],
52 FALSE
53 );
54
55 $form->addSelect('format_id', [
56 'label' => ts('Select Format'),
57 'placeholder' => ts('Default'),
58 'entity' => 'message_template',
59 'field' => 'pdf_format_id',
60 'option_url' => 'civicrm/admin/pdfFormats',
61 ]);
62 $form->add(
63 'select',
64 'paper_size',
65 ts('Paper Size'),
66 [0 => ts('- default -')] + CRM_Core_BAO_PaperSize::getList(TRUE),
67 FALSE,
68 ['onChange' => "selectPaper( this.value ); showUpdateFormatChkBox();"]
69 );
70 $form->add(
71 'select',
72 'orientation',
73 ts('Orientation'),
74 CRM_Core_BAO_PdfFormat::getPageOrientations(),
75 FALSE,
76 ['onChange' => "updatePaperDimensions(); showUpdateFormatChkBox();"]
77 );
78 $form->add(
79 'select',
80 'metric',
81 ts('Unit of Measure'),
82 CRM_Core_BAO_PdfFormat::getUnits(),
83 FALSE,
84 ['onChange' => "selectMetric( this.value );"]
85 );
86 $form->add(
87 'text',
88 'margin_left',
89 ts('Left Margin'),
90 ['size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"],
91 TRUE
92 );
93 $form->add(
94 'text',
95 'margin_right',
96 ts('Right Margin'),
97 ['size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"],
98 TRUE
99 );
100 $form->add(
101 'text',
102 'margin_top',
103 ts('Top Margin'),
104 ['size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"],
105 TRUE
106 );
107 $form->add(
108 'text',
109 'margin_bottom',
110 ts('Bottom Margin'),
111 ['size' => 8, 'maxlength' => 8, 'onkeyup' => "showUpdateFormatChkBox();"],
112 TRUE
113 );
114
115 $config = CRM_Core_Config::singleton();
116 /** CRM-15883 Suppressing Stationery path field until we switch from DOMPDF to a library that supports it.
117 * if ($config->wkhtmltopdfPath == FALSE) {
118 * $form->add(
119 * 'text',
120 * 'stationery',
121 * ts('Stationery (relative path to PDF you wish to use as the background)'),
122 * array('size' => 25, 'maxlength' => 900, 'onkeyup' => "showUpdateFormatChkBox();"),
123 * FALSE
124 * );
125 * }
126 */
127 $form->add('checkbox', 'bind_format', ts('Always use this Page Format with the selected Template'));
128 $form->add('checkbox', 'update_format', ts('Update Page Format (this will affect all templates that use this format)'));
129
130 $form->assign('useThisPageFormat', ts('Always use this Page Format with the new template?'));
131 $form->assign('useSelectedPageFormat', ts('Should the new template always use the selected Page Format?'));
132 $form->assign('totalSelectedContacts', !is_null($form->_contactIds) ? count($form->_contactIds) : 0);
133
134 $form->add('select', 'document_type', ts('Document Type'), CRM_Core_SelectValues::documentFormat());
135
136 $documentTypes = implode(',', CRM_Core_SelectValues::documentApplicationType());
137 $form->addElement('file', "document_file", 'Upload Document', 'size=30 maxlength=255 accept="' . $documentTypes . '"');
138 $form->addUploadElement("document_file");
139
140 CRM_Mailing_BAO_Mailing::commonCompose($form);
141
142 $buttons = [];
143 if ($form->get('action') != CRM_Core_Action::VIEW) {
144 $buttons[] = [
145 'type' => 'upload',
146 'name' => ts('Download Document'),
147 'isDefault' => TRUE,
148 'icon' => 'fa-download',
149 ];
150 $buttons[] = [
151 'type' => 'submit',
152 'name' => ts('Preview'),
153 'subName' => 'preview',
154 'icon' => 'fa-search',
155 'isDefault' => FALSE,
156 ];
157 }
158 $buttons[] = [
159 'type' => 'cancel',
160 'name' => $form->get('action') == CRM_Core_Action::VIEW ? ts('Done') : ts('Cancel'),
161 ];
162 $form->addButtons($buttons);
163
164 $form->addFormRule(['CRM_Core_Form_Task_PDFLetterCommon', 'formRule'], $form);
165 }
166
167 /**
168 * Set default values.
169 */
170 public static function setDefaultValues() {
171 $defaultFormat = CRM_Core_BAO_PdfFormat::getDefaultValues();
172 $defaultFormat['format_id'] = $defaultFormat['id'];
173 return $defaultFormat;
174 }
175
176 /**
177 * Form rule.
178 *
179 * @param array $fields
180 * The input form values.
181 * @param array $files
182 * @param array $self
183 * Additional values form 'this'.
184 *
185 * @return bool
186 * TRUE if no errors, else array of errors.
187 */
188 public static function formRule($fields, $files, $self) {
189 $errors = [];
190 $template = CRM_Core_Smarty::singleton();
191
192 // If user uploads non-document file other than odt/docx
193 if (empty($fields['template']) &&
194 !empty($files['document_file']['tmp_name']) &&
195 array_search($files['document_file']['type'], CRM_Core_SelectValues::documentApplicationType()) == NULL
196 ) {
197 $errors['document_file'] = ts('Invalid document file format');
198 }
199 //Added for CRM-1393
200 if (!empty($fields['saveTemplate']) && empty($fields['saveTemplateName'])) {
201 $errors['saveTemplateName'] = ts("Enter name to save message template");
202 }
203 if (!is_numeric($fields['margin_left'])) {
204 $errors['margin_left'] = 'Margin must be numeric';
205 }
206 if (!is_numeric($fields['margin_right'])) {
207 $errors['margin_right'] = 'Margin must be numeric';
208 }
209 if (!is_numeric($fields['margin_top'])) {
210 $errors['margin_top'] = 'Margin must be numeric';
211 }
212 if (!is_numeric($fields['margin_bottom'])) {
213 $errors['margin_bottom'] = 'Margin must be numeric';
214 }
215 return empty($errors) ? TRUE : $errors;
216 }
217
218 /**
219 * Handle the template processing part of the form
220 *
221 * @param array $formValues
222 *
223 * @return string $html_message
224 *
225 * @throws \CiviCRM_API3_Exception
226 * @throws \Civi\API\Exception\UnauthorizedException
227 */
228 public static function processTemplate(&$formValues) {
229 $html_message = $formValues['html_message'] ?? NULL;
230
231 // process message template
232 if (!empty($formValues['saveTemplate']) || !empty($formValues['updateTemplate'])) {
233 $messageTemplate = [
234 'msg_text' => NULL,
235 'msg_html' => $formValues['html_message'],
236 'msg_subject' => NULL,
237 'is_active' => TRUE,
238 ];
239
240 $messageTemplate['pdf_format_id'] = 'null';
241 if (!empty($formValues['bind_format']) && $formValues['format_id']) {
242 $messageTemplate['pdf_format_id'] = $formValues['format_id'];
243 }
244 if (!empty($formValues['saveTemplate']) && $formValues['saveTemplate']) {
245 $messageTemplate['msg_title'] = $formValues['saveTemplateName'];
246 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
247 }
248
249 if (!empty($formValues['updateTemplate']) && $formValues['template'] && $formValues['updateTemplate']) {
250 $messageTemplate['id'] = $formValues['template'];
251
252 unset($messageTemplate['msg_title']);
253 CRM_Core_BAO_MessageTemplate::add($messageTemplate);
254 }
255 }
256 elseif (CRM_Utils_Array::value('template', $formValues) > 0) {
257 if (!empty($formValues['bind_format']) && $formValues['format_id']) {
258 $query = "UPDATE civicrm_msg_template SET pdf_format_id = {$formValues['format_id']} WHERE id = {$formValues['template']}";
259 }
260 else {
261 $query = "UPDATE civicrm_msg_template SET pdf_format_id = NULL WHERE id = {$formValues['template']}";
262 }
263 CRM_Core_DAO::executeQuery($query);
264
265 $documentInfo = CRM_Core_BAO_File::getEntityFile('civicrm_msg_template', $formValues['template']);
266 foreach ((array) $documentInfo as $info) {
267 list($html_message, $formValues['document_type']) = CRM_Utils_PDF_Document::docReader($info['fullPath'], $info['mime_type']);
268 $formValues['document_file_path'] = $info['fullPath'];
269 }
270 }
271 // extract the content of uploaded document file
272 elseif (!empty($formValues['document_file'])) {
273 list($html_message, $formValues['document_type']) = CRM_Utils_PDF_Document::docReader($formValues['document_file']['name'], $formValues['document_file']['type']);
274 $formValues['document_file_path'] = $formValues['document_file']['name'];
275 }
276
277 if (!empty($formValues['update_format'])) {
278 $bao = new CRM_Core_BAO_PdfFormat();
279 $bao->savePdfFormat($formValues, $formValues['format_id']);
280 }
281
282 return $html_message;
283 }
284
285 /**
286 * @param $message
287 */
288 public static function formatMessage(&$message) {
289 $newLineOperators = [
290 'p' => [
291 'oper' => '<p>',
292 'pattern' => '/<(\s+)?p(\s+)?>/m',
293 ],
294 'br' => [
295 'oper' => '<br />',
296 'pattern' => '/<(\s+)?br(\s+)?\/>/m',
297 ],
298 ];
299
300 $htmlMsg = preg_split($newLineOperators['p']['pattern'], $message);
301 foreach ($htmlMsg as $k => & $m) {
302 $messages = preg_split($newLineOperators['br']['pattern'], $m);
303 foreach ($messages as $key => & $msg) {
304 $msg = trim($msg);
305 $matches = [];
306 if (preg_match('/^(&nbsp;)+/', $msg, $matches)) {
307 $spaceLen = strlen($matches[0]) / 6;
308 $trimMsg = ltrim($msg, '&nbsp; ');
309 $charLen = strlen($trimMsg);
310 $totalLen = $charLen + $spaceLen;
311 if ($totalLen > 100) {
312 $spacesCount = 10;
313 if ($spaceLen > 50) {
314 $spacesCount = 20;
315 }
316 if ($charLen > 100) {
317 $spacesCount = 1;
318 }
319 $msg = str_repeat('&nbsp;', $spacesCount) . $trimMsg;
320 }
321 }
322 }
323 $m = implode($newLineOperators['br']['oper'], $messages);
324 }
325 $message = implode($newLineOperators['p']['oper'], $htmlMsg);
326 }
327
328 /**
329 * Render html from rows
330 *
331 * @param $rows
332 * @param string $msgPart
333 * The name registered with the TokenProcessor
334 * @param array $formValues
335 * The values submitted through the form
336 *
337 * @return array
338 * If formValues['is_unit_test'] is true, otherwise outputs document to browser
339 */
340 public static function renderFromRows($rows, $msgPart, $formValues) {
341 $html = [];
342 foreach ($rows as $row) {
343 $html[] = $row->render($msgPart);
344 }
345
346 if (!empty($formValues['is_unit_test'])) {
347 return $html;
348 }
349
350 if (!empty($html)) {
351 self::outputFromHtml($formValues, $html);
352 }
353 }
354
355 /**
356 * List the available tokens
357 * @return array of token name => label
358 */
359 public static function listTokens() {
360 $class = get_called_class();
361 if (method_exists($class, 'createTokenProcessor')) {
362 return $class::createTokenProcessor()->listTokens();
363 }
364 }
365
366 /**
367 * Output the pdf or word document from the generated html.
368 *
369 * @param array $formValues
370 * @param array $html
371 */
372 protected static function outputFromHtml($formValues, array $html) {
373 if ($formValues['document_type'] === 'pdf') {
374 CRM_Utils_PDF_Utils::html2pdf($html, 'CiviLetter.pdf', FALSE, $formValues);
375 }
376 else {
377 CRM_Utils_PDF_Document::html2doc($html, 'CiviLetter.' . $formValues['document_type'], $formValues);
378 }
379 }
380
381 }