e501e95ff7515f35933e7b46b94769b61760276c
[civicrm-core.git] / CRM / Badge / BAO / Badge.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 * Class CRM_Badge_Format_Badge.
20 *
21 * parent class for building name badges
22 */
23 class CRM_Badge_BAO_Badge {
24
25 /**
26 * @var bool
27 */
28 public $debug = FALSE;
29
30 /**
31 * @var int
32 */
33 public $border = 0;
34
35 /**
36 * This function is called to create name label pdf.
37 *
38 * @param array $participants
39 * Associated array with participant info.
40 * @param array $layoutInfo
41 * Associated array which contains meta data about format/layout.
42 */
43 public function createLabels($participants, &$layoutInfo) {
44 $this->pdf = new CRM_Utils_PDF_Label($layoutInfo['format'], 'mm');
45 $this->pdf->Open();
46 $this->pdf->setPrintHeader(FALSE);
47 $this->pdf->setPrintFooter(FALSE);
48 $this->pdf->AddPage();
49 $this->pdf->SetGenerator($this, "generateLabel");
50
51 // this is very useful for debugging, by default set to FALSE
52 if ($this->debug) {
53 $this->border = "LTRB";
54 }
55
56 foreach ($participants as $participant) {
57 $formattedRow = self::formatLabel($participant, $layoutInfo);
58 $this->pdf->AddPdfLabel($formattedRow);
59 }
60
61 if (CIVICRM_UF === 'UnitTests') {
62 throw new CRM_Core_Exception_PrematureExitException('pdf output called', ['formattedRow' => $formattedRow]);
63 }
64 $this->pdf->Output(CRM_Utils_String::munge($layoutInfo['title'], '_', 64) . '.pdf', 'D');
65 CRM_Utils_System::civiExit();
66 }
67
68 /**
69 * Function to create structure and add meta data according to layout.
70 *
71 * @param array $row
72 * Row element that needs to be formatted.
73 * @param array $layout
74 * Layout meta data.
75 *
76 * @return array
77 * row with meta data
78 */
79 public static function formatLabel(&$row, &$layout) {
80 $formattedRow = ['labelFormat' => $layout['label_format_name']];
81 $formattedRow['labelTitle'] = $layout['title'];
82 $formattedRow['labelId'] = $layout['id'];
83
84 if (!empty($layout['data']['rowElements'])) {
85 foreach ($layout['data']['rowElements'] as $key => $element) {
86 $value = '';
87 if ($element) {
88 $value = $row[$element];
89 // hack to fix date field display format
90 if (strpos($element, '_date')) {
91 $value = CRM_Utils_Date::customFormat($value, "%B %E%f");
92 }
93 }
94
95 $formattedRow['token'][$key] = [
96 'value' => $value,
97 'font_name' => $layout['data']['font_name'][$key],
98 'font_size' => $layout['data']['font_size'][$key],
99 'font_style' => $layout['data']['font_style'][$key],
100 'text_alignment' => $layout['data']['text_alignment'][$key],
101 'token' => $layout['data']['token'][$key],
102 ];
103 }
104 }
105
106 if (!empty($layout['data']['image_1'])) {
107 $formattedRow['image_1'] = $layout['data']['image_1'];
108 }
109 if (!empty($layout['data']['width_image_1'])) {
110 $formattedRow['width_image_1'] = $layout['data']['width_image_1'];
111 }
112 if (!empty($layout['data']['height_image_1'])) {
113 $formattedRow['height_image_1'] = $layout['data']['height_image_1'];
114 }
115
116 if (!empty($layout['data']['image_2'])) {
117 $formattedRow['image_2'] = $layout['data']['image_2'];
118 }
119 if (!empty($layout['data']['width_image_2'])) {
120 $formattedRow['width_image_2'] = $layout['data']['width_image_2'];
121 }
122 if (!empty($layout['data']['height_image_2'])) {
123 $formattedRow['height_image_2'] = $layout['data']['height_image_2'];
124 }
125 if (!empty($row['image_URL']) && !empty($layout['data']['show_participant_image'])) {
126 $formattedRow['participant_image'] = $row['image_URL'];
127 }
128 if (!empty($layout['data']['width_participant_image'])) {
129 $formattedRow['width_participant_image'] = $layout['data']['width_participant_image'];
130 }
131 if (!empty($layout['data']['height_participant_image'])) {
132 $formattedRow['height_participant_image'] = $layout['data']['height_participant_image'];
133 }
134 if (!empty($layout['data']['alignment_participant_image'])) {
135 $formattedRow['alignment_participant_image'] = $layout['data']['alignment_participant_image'];
136 }
137
138 if (!empty($layout['data']['add_barcode'])) {
139 $formattedRow['barcode'] = [
140 'alignment' => $layout['data']['barcode_alignment'],
141 'type' => $layout['data']['barcode_type'],
142 ];
143 }
144
145 // finally assign all the row values, so that we can use it for barcode etc
146 $formattedRow['values'] = $row;
147
148 return $formattedRow;
149 }
150
151 /**
152 * @param $formattedRow
153 */
154 public function generateLabel($formattedRow) {
155 switch ($formattedRow['labelFormat']) {
156 case 'A6 Badge Portrait 150x106':
157 case 'Hanging Badge 3-3/4" x 4-3"/4':
158 self::labelCreator($formattedRow, 5);
159 break;
160
161 case 'Avery 5395':
162 default:
163 self::labelCreator($formattedRow);
164 break;
165 }
166 }
167
168 /**
169 * @param $formattedRow
170 * @param int $cellspacing
171 */
172 public function labelCreator(&$formattedRow, $cellspacing = 0) {
173 $this->lMarginLogo = 18;
174 $this->tMarginName = 20;
175
176 $x = $this->pdf->GetAbsX();
177 $y = $this->pdf->getY();
178
179 //call hook alterBadge
180 CRM_Utils_Hook::alterBadge($formattedRow['labelTitle'], $this, $formattedRow, $formattedRow['values']);
181
182 $startOffset = 0;
183 if (!empty($formattedRow['image_1'])) {
184 $this->printImage($formattedRow['image_1'], NULL, NULL, CRM_Utils_Array::value('width_image_1', $formattedRow),
185 CRM_Utils_Array::value('height_image_1', $formattedRow));
186 }
187
188 if (!empty($formattedRow['image_2'])) {
189 $this->printImage($formattedRow['image_2'], $x + 68, NULL, CRM_Utils_Array::value('width_image_2', $formattedRow),
190 CRM_Utils_Array::value('height_image_2', $formattedRow));
191 }
192
193 if ((CRM_Utils_Array::value('height_image_1', $formattedRow) >
194 CRM_Utils_Array::value('height_image_2', $formattedRow)) && !empty($formattedRow['height_image_1'])
195 ) {
196 $startOffset = $formattedRow['height_image_1'] ?? NULL;
197 }
198 elseif (!empty($formattedRow['height_image_2'])) {
199 $startOffset = $formattedRow['height_image_2'] ?? NULL;
200 }
201
202 if (!empty($formattedRow['participant_image'])) {
203 $imageAlign = 0;
204 switch (CRM_Utils_Array::value('alignment_participant_image', $formattedRow)) {
205 case 'R':
206 $imageAlign = 68;
207 break;
208
209 case 'L':
210 $imageAlign = 0;
211 break;
212
213 default:
214 break;
215 }
216 $this->pdf->Image($formattedRow['participant_image'], $x + $imageAlign, $y + $startOffset, CRM_Utils_Array::value('width_participant_image', $formattedRow), CRM_Utils_Array::value('height_participant_image', $formattedRow));
217 if ($startOffset == NULL && !empty($formattedRow['height_participant_image'])) {
218 $startOffset = $formattedRow['height_participant_image'];
219 }
220 }
221
222 $this->pdf->SetLineStyle([
223 'width' => 0.1,
224 'cap' => 'round',
225 'join' => 'round',
226 'dash' => '2,2',
227 'color' => [0, 0, 200],
228 ]);
229
230 $rowCount = CRM_Badge_Form_Layout::FIELD_ROWCOUNT;
231 for ($i = 1; $i <= $rowCount; $i++) {
232 if (!empty($formattedRow['token'][$i]['token'])) {
233 $value = '';
234 if ($formattedRow['token'][$i]['token'] != 'spacer') {
235 $value = $formattedRow['token'][$i]['value'];
236 }
237
238 $xAlign = $x;
239 $rowWidth = $this->pdf->width;
240 if (!empty($formattedRow['participant_image']) && !empty($formattedRow['width_participant_image'])) {
241 $rowWidth = $this->pdf->width - $formattedRow['width_participant_image'];
242 if ($formattedRow['alignment_participant_image'] == 'L') {
243 $xAlign = $x + $formattedRow['width_participant_image'] + $imageAlign;
244 }
245 }
246 $offset = $this->pdf->getY() + $startOffset + $cellspacing;
247
248 $this->pdf->SetFont($formattedRow['token'][$i]['font_name'], $formattedRow['token'][$i]['font_style'],
249 $formattedRow['token'][$i]['font_size']);
250 $this->pdf->MultiCell($rowWidth, 0, $value,
251 $this->border, $formattedRow['token'][$i]['text_alignment'], 0, 1, $xAlign, $offset);
252
253 // set this to zero so that it is added only for first element
254 $startOffset = 0;
255 }
256 }
257
258 if (!empty($formattedRow['barcode'])) {
259 $data = $formattedRow['values'];
260
261 if ($formattedRow['barcode']['type'] == 'barcode') {
262 $data['current_value'] = $formattedRow['values']['contact_id'] . '-' . $formattedRow['values']['participant_id'];
263 }
264 else {
265 // view participant url
266 $data['current_value'] = CRM_Utils_System::url('civicrm/contact/view/participant',
267 'action=view&reset=1&cid=' . $formattedRow['values']['contact_id'] . '&id='
268 . $formattedRow['values']['participant_id'],
269 TRUE,
270 NULL,
271 FALSE
272 );
273 }
274
275 // call hook alterBarcode
276 CRM_Utils_Hook::alterBarcode($data, $formattedRow['barcode']['type']);
277
278 if ($formattedRow['barcode']['type'] == 'barcode') {
279 // barcode position
280 $xAlign = $x;
281
282 switch ($formattedRow['barcode']['alignment']) {
283 case 'L':
284 $xAlign += -14;
285 break;
286
287 case 'R':
288 $xAlign += 27;
289 break;
290
291 case 'C':
292 $xAlign += 9;
293 break;
294 }
295
296 $style = [
297 'position' => '',
298 'align' => '',
299 'stretch' => FALSE,
300 'fitwidth' => TRUE,
301 'cellfitalign' => '',
302 'border' => FALSE,
303 'hpadding' => 13.5,
304 'vpadding' => 'auto',
305 'fgcolor' => [0, 0, 0],
306 'bgcolor' => FALSE,
307 'text' => FALSE,
308 'font' => 'helvetica',
309 'fontsize' => 8,
310 'stretchtext' => 0,
311 ];
312
313 $this->pdf->write1DBarcode($data['current_value'], 'C128', $xAlign, $y + $this->pdf->height - 10, '70',
314 12, 0.4, $style, 'B');
315 }
316 else {
317 // qr code position
318 $xAlign = $x;
319
320 switch ($formattedRow['barcode']['alignment']) {
321 case 'L':
322 $xAlign += -5;
323 break;
324
325 case 'R':
326 $xAlign += 56;
327 break;
328
329 case 'C':
330 $xAlign += 29;
331 break;
332 }
333
334 $style = [
335 'border' => FALSE,
336 'hpadding' => 13.5,
337 'vpadding' => 'auto',
338 'fgcolor' => [0, 0, 0],
339 'bgcolor' => FALSE,
340 'position' => '',
341 ];
342
343 $this->pdf->write2DBarcode($data['current_value'], 'QRCODE,H', $xAlign, $y + $this->pdf->height - 26, 30,
344 30, $style, 'B');
345 }
346 }
347 }
348
349 /**
350 * Helper function to print images.
351 *
352 * @param string $img
353 * Image url.
354 *
355 * @param string $x
356 * @param string $y
357 * @param null $w
358 * @param null $h
359 */
360 public function printImage($img, $x = '', $y = '', $w = NULL, $h = NULL) {
361 if (!$x) {
362 $x = $this->pdf->GetAbsX();
363 }
364
365 if (!$y) {
366 $y = $this->pdf->GetY();
367 }
368
369 $this->imgRes = 300;
370
371 if ($img) {
372 list($w, $h) = self::getImageProperties($img, $this->imgRes, $w, $h);
373 $this->pdf->Image($img, $x, $y, $w, $h, '', '', '', FALSE, 72, '', FALSE,
374 FALSE, $this->debug, FALSE, FALSE, FALSE);
375 }
376 $this->pdf->SetXY($x, $y);
377 }
378
379 /**
380 * @param $img
381 * @param int $imgRes
382 * @param null $w
383 * @param null $h
384 *
385 * @return array
386 */
387 public static function getImageProperties($img, $imgRes = 300, $w = NULL, $h = NULL) {
388 $imgsize = getimagesize($img);
389 $f = $imgRes / 25.4;
390 $w = !empty($w) ? $w : $imgsize[0] / $f;
391 $h = !empty($h) ? $h : $imgsize[1] / $f;
392 return [$w, $h];
393 }
394
395 /**
396 * Build badges parameters before actually creating badges.
397 *
398 * @param array $params
399 * Associated array of submitted values.
400 * @param CRM_Core_Form $form
401 */
402 public static function buildBadges(&$params, &$form) {
403 // get name badge layout info
404 $layoutInfo = CRM_Badge_BAO_Layout::buildLayout($params);
405
406 // split/get actual field names from token and individual contact image URLs
407 $returnProperties = [];
408 if (!empty($layoutInfo['data']['token'])) {
409 foreach ($layoutInfo['data']['token'] as $index => $value) {
410 $element = '';
411 if ($value) {
412 $token = CRM_Utils_Token::getTokens($value);
413 if (key($token) == 'contact') {
414 $element = $token['contact'][0];
415 }
416 elseif (key($token) == 'event') {
417 $element = $token['event'][0];
418 //FIX ME - we need to standardize event token names
419 if (substr($element, 0, 6) != 'event_') {
420 $element = 'event_' . $element;
421 }
422 }
423 elseif (key($token) == 'participant') {
424 $element = $token['participant'][0];
425 }
426
427 // build returnproperties for query
428 $returnProperties[$element] = 1;
429 }
430
431 // add actual field name to row element
432 $layoutInfo['data']['rowElements'][$index] = $element;
433 }
434 }
435
436 // add additional required fields for query execution
437 $additionalFields = ['participant_register_date', 'participant_id', 'event_id', 'contact_id', 'image_URL'];
438 foreach ($additionalFields as $field) {
439 $returnProperties[$field] = 1;
440 }
441
442 if ($form->_single) {
443 $queryParams = NULL;
444 }
445 else {
446 $queryParams = $form->get('queryParams');
447 }
448
449 $query = new CRM_Contact_BAO_Query($queryParams, $returnProperties, NULL, FALSE, FALSE,
450 CRM_Contact_BAO_Query::MODE_EVENT
451 );
452
453 list($select, $from, $where, $having) = $query->query();
454 if (empty($where)) {
455 $where = "WHERE {$form->_componentClause}";
456 }
457 else {
458 $where .= " AND {$form->_componentClause}";
459 }
460
461 $sortOrder = NULL;
462 if ($form->get(CRM_Utils_Sort::SORT_ORDER)) {
463 $sortOrder = $form->get(CRM_Utils_Sort::SORT_ORDER);
464 if (!empty($sortOrder)) {
465 $sortOrder = " ORDER BY $sortOrder";
466 }
467 }
468 $queryString = "$select $from $where $having $sortOrder";
469
470 $dao = CRM_Core_DAO::executeQuery($queryString);
471 $rows = [];
472 while ($dao->fetch()) {
473 $query->convertToPseudoNames($dao);
474 $rows[$dao->participant_id] = [];
475 foreach ($returnProperties as $key => $dontCare) {
476 $value = $dao->$key ?? NULL;
477 // Format custom fields
478 if (strstr($key, 'custom_') && isset($value)) {
479 $value = CRM_Core_BAO_CustomField::displayValue($value, substr($key, 7), $dao->contact_id);
480 }
481 $rows[$dao->participant_id][$key] = $value;
482 }
483 }
484
485 $eventBadgeClass = new CRM_Badge_BAO_Badge();
486 $eventBadgeClass->createLabels($rows, $layoutInfo);
487 }
488
489 }