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