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