PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.6.7
VikBooking Hotel Booking Engine & PMS v1.6.7
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
vikbooking / admin / helpers / einvoicing / einvoicing.php

einvoicing.php in VikBooking Hotel Booking Engine & PMS 1.6.7, at admin/helpers/einvoicing/einvoicing.php

1,136 lines 26.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 /**
14 * Electronic Invoicing abstract class.
15 */
16 abstract class VikBookingEInvoicing
17 {
18 /**
19 * @var string
20 */
21 protected $driverFile = '';
22
23 /**
24 * @var string
25 */
26 protected $driverName = '';
27
28 /**
29 * @var int
30 */
31 protected $driverId = null;
32
33 /**
34 * @var array
35 */
36 protected $driverFilters = [];
37
38 /**
39 * @var array
40 */
41 protected $driverButtons = [];
42
43 /**
44 * @var string
45 */
46 protected $driverScript = '';
47
48 /**
49 * @var bool
50 */
51 protected $hasSettings = false;
52
53 /**
54 * @var array
55 */
56 protected $warning = [];
57
58 /**
59 * @var array
60 */
61 protected $error = [];
62
63 /**
64 * @var array
65 */
66 protected $info = [];
67
68 /**
69 * @var object
70 */
71 protected $dbo;
72
73 /**
74 * @var object
75 */
76 protected $session;
77
78 /**
79 * @var array
80 */
81 protected $cols = [];
82
83 /**
84 * @var array
85 */
86 protected $rows = [];
87
88 /**
89 * @var array
90 */
91 protected $footerRow = [];
92
93 /**
94 * @var mixed flag modifiable by external invokes (cron, analogic invoices)
95 */
96 public $externalCall = null;
97
98 /**
99 * @var array data injectable by external callers
100 */
101 public $externalData = [];
102
103 /**
104 * @var array
105 *
106 * @since 1.16.7 (J) - 1.6.7 (WP)
107 */
108 public $driverSettings = [];
109
110 /**
111 * Class constructor should define some vars of the driver in use.
112 */
113 public function __construct()
114 {
115 $this->dbo = JFactory::getDbo();
116 $this->session = JFactory::getSession();
117
118 /**
119 * Turn on the flag that the eInvocing class is running
120 * so that other parts of the framework can avoid to
121 * invoke again this class and to generate double e-invoices.
122 */
123 defined('VBO_EINVOICING_RUN') OR define('VBO_EINVOICING_RUN', 1);
124 }
125
126 /**
127 * Extending Classes should define this method
128 * to get the name of class file.
129 */
130 abstract public function getFileName();
131
132 /**
133 * Extending Classes should define this method
134 * to get the name of the driver.
135 */
136 abstract public function getName();
137
138 /**
139 * Extending Classes should define this method
140 * to get the filters of the driver.
141 */
142 abstract public function getFilters();
143
144 /**
145 * Extending Classes should define this method
146 * to get the driver action buttons.
147 */
148 abstract public function getButtons();
149
150 /**
151 * Extending Classes should define this method
152 * to generate the bookings data (cols and rows).
153 */
154 abstract public function getBookingsData();
155
156 /**
157 * Extending Classes should define this method
158 * to prepare the settings of the driver before saving.
159 * Views should not call this method.
160 */
161 abstract protected function prepareSavingSettings();
162
163 /**
164 * Extending Classes should define this method
165 * to generate the electronic invoices.
166 */
167 abstract public function generateEInvoices();
168
169 /**
170 * Extending Classes should define this method
171 * to generate the electronic invoice for
172 * the given booking ID or booking array.
173 */
174 abstract public function generateEInvoice($data);
175
176 /**
177 * Extending Classes should define this method
178 * to generate the electronic invoice from a custom
179 * invoice not related to any booking ID.
180 * Existing (analogic) invoice record and customer
181 * shall be passed as arguments.
182 */
183 abstract public function prepareCustomInvoiceData($invoice, $customer);
184
185 /**
186 * Extending Classes should define this method
187 * to check if an electronic invoice exists for
188 * a given booking ID or Number. This is useful
189 * for external scripts to request later the
190 * obliteration of the invoice to create a new one.
191 * For example, during the update of a custom invoice.
192 */
193 abstract public function eInvoiceExists($data);
194
195 /**
196 * Extending Classes should define this method
197 * to obliterate an existingelectronic invoice.
198 * This is useful for external scripts to request the
199 * obliteration of the invoice to create a new one.
200 * For example, during the update of a custom invoice.
201 */
202 abstract public function obliterateEInvoice($data);
203
204 /**
205 * Drivers can override this method to manipulate the reservation details
206 * at runtime, in case some values should be altered for certain reasons
207 * (i.e. environmental fee detucted and invoiced separately).
208 *
209 * @param array &$booking the reservation record to elaborate (may be nested).
210 * @param array &$rooms the room reservation records to elaborate.
211 *
212 * @return void
213 *
214 * @since 1.16.7 (J) - 1.6.7 (WP)
215 */
216 public function elaborateBookingDetails(array &$booking, array &$rooms = [])
217 {
218 return;
219 }
220
221 /**
222 * Drivers can override this method to register additional PDF invoice
223 * details for a specific reservation ID (i.e. a correlated invoice).
224 *
225 * @param int $bid the reservation record ID.
226 *
227 * @return array
228 *
229 * @since 1.16.7 (J) - 1.6.7 (WP)
230 */
231 public function getBookingExtraInvoices($bid)
232 {
233 return [];
234 }
235
236 /**
237 * Loads the settings for the driver, which parameters
238 * must be saved as a JSON encoded string. Never cache
239 * the settings fetched as they may be updated by other methods.
240 *
241 * @return mixed array if settings exist, false otherwise
242 */
243 protected function loadSettings()
244 {
245 if ($this->driverSettings) {
246 return $this->driverSettings;
247 }
248
249 $this->dbo->setQuery(
250 $this->dbo->getQuery(true)
251 ->select('*')
252 ->from($this->dbo->qn('#__vikbooking_einvoicing_config'))
253 ->where($this->dbo->qn('driver') . ' = ' . $this->dbo->q($this->getFileName()))
254 ->order($this->dbo->qn('id') . ' DESC')
255 , 0, 1);
256 $settings = $this->dbo->loadAssoc();
257
258 if (!$settings) {
259 // no settings defined for this driver
260 return false;
261 }
262
263 $settings['params'] = !empty($settings['params']) ? json_decode($settings['params'], true) : array();
264 $settings['params'] = is_array($settings['params']) ? $settings['params'] : array();
265
266 // update driverId
267 $this->driverId = $settings['id'];
268
269 // cache settings
270 $this->driverSettings = $settings;
271
272 // return array of settings with decoded parameters
273 return $this->driverSettings;
274 }
275
276 /**
277 * Resets and reloads the driver settings to avoid cache.
278 *
279 * @return array
280 *
281 * @since 1.16.7 (J) - 1.6.7 (WP)
282 */
283 protected function reloadSettings()
284 {
285 // reset
286 $this->driverSettings = [];
287
288 // reload
289 $this->loadSettings();
290
291 return $this->driverSettings;
292 }
293
294 /**
295 * Saves the settings for the driver.
296 * The View may call this method to save the driver settings.
297 *
298 * @return boolean true if settings were stored, false otherwise
299 */
300 public function saveSettings()
301 {
302 $data = $this->prepareSavingSettings();
303 if (!$data instanceof stdClass || !get_object_vars($data)) {
304 return false;
305 }
306
307 $q = "SELECT * FROM `#__vikbooking_einvoicing_config` WHERE `driver`=".$this->dbo->quote($this->getFileName())." ORDER BY `id` DESC LIMIT 1;";
308 $this->dbo->setQuery($q);
309 $current = $this->dbo->loadAssoc();
310 if (!$current) {
311 // create new driver record as it's the first time we're saving the settings
312 if (!$this->dbo->insertObject('#__vikbooking_einvoicing_config', $data, 'id')) {
313 return false;
314 }
315 } else {
316 // update the driver record with the new settings
317 $data->id = $current['id'];
318 if (!$this->dbo->updateObject('#__vikbooking_einvoicing_config', $data, 'id')) {
319 return false;
320 }
321 }
322
323 $this->setInfo(JText::translate('VBODRIVERSETTSUPD'));
324
325 return true;
326 }
327
328 /**
329 * Updates one setting in the current driver configuration.
330 *
331 * @param string $name the setting name to set.
332 * @param mixed $value the setting value to set.
333 *
334 * @return bool
335 *
336 * @since 1.16.7 (J) - 1.6.7 (WP)
337 */
338 public function updateDriverSetting($name, $value = null)
339 {
340 $settings = $this->loadSettings();
341 $settings['params'][$name] = $value;
342
343 $record = new stdClass;
344 $record->id = $this->getDriverId();
345 $record->params = json_encode($settings['params']);
346
347 $result = (bool)$this->dbo->updateObject('#__vikbooking_einvoicing_config', $record, 'id');
348
349 // reload driver settings
350 $this->reloadSettings();
351
352 return $result;
353 }
354
355 /**
356 * Returns whether the driver has settings to be defined.
357 *
358 * @return boolean
359 */
360 public function hasSettings()
361 {
362 return $this->hasSettings;
363 }
364
365 /**
366 * Echoes the HTML required for the driver settings form.
367 * This method should be extended by the driver if settings are needed.
368 *
369 * @return void
370 */
371 public function printSettings()
372 {
373 return;
374 }
375
376 /**
377 * Echoes the HTML required for the driver overlay form.
378 * This method should be extended by the driver if needed.
379 * For example, to show contents within a modal.
380 *
381 * @return void
382 */
383 public function printOverlayContent()
384 {
385 return;
386 }
387
388 /**
389 * Returns the ID of the current driver by taking it from the settings.
390 * By calling loadSettings(), if some settings are defined, the ID is set in driverId.
391 *
392 * @return mixed the ID of the current driver if some settings were stored or null.
393 *
394 * @uses loadSettings
395 */
396 protected function getDriverId()
397 {
398 if (is_null($this->driverId)) {
399 // this method will set a value for the property driverId if some settings were saved
400 $this->loadSettings();
401 }
402
403 return $this->driverId;
404 }
405
406 /**
407 * Stores an electronic invoice. If the booking ID is passed through the data
408 * the system will set to obliterated all e-invoices previously made for that booking.
409 * Custom (manual) invoices should be obliterated by the external caller script instead.
410 *
411 * @param object $data stdClass object with the data to store
412 *
413 * @return mixed the ID of the invoice stored, false otherwise
414 */
415 protected function storeEInvoice($data)
416 {
417 if (!$data instanceof stdClass || !get_object_vars($data)) {
418 return false;
419 }
420
421 if (isset($data->idorder) && (int)$data->idorder > 0) {
422 // obliterate any possible previous e-invoice for this booking
423 $q = "UPDATE `#__vikbooking_einvoicing_data` SET `obliterated`=1 WHERE `driverid`=".(int)$this->driverId." AND `idorder`=".(int)$data->idorder.";";
424 $this->dbo->setQuery($q);
425 $this->dbo->execute();
426 }
427
428 if ($this->dbo->insertObject('#__vikbooking_einvoicing_data', $data, 'id')) {
429 return $data->id;
430 }
431
432 return false;
433 }
434
435 /**
436 * Updates the information for an electronic invoice.
437 *
438 * @param object $data stdClass object with the data to store
439 *
440 * @return boolean
441 */
442 protected function updateEInvoice($data)
443 {
444 if (!$data instanceof stdClass || !get_object_vars($data)) {
445 return false;
446 }
447
448 if ($this->dbo->updateObject('#__vikbooking_einvoicing_data', $data, 'id')) {
449 return true;
450 }
451
452 return false;
453 }
454
455 /**
456 * Checks whether an analogic invoice in PDF was already issued
457 * for the given booking ID. This is to avoid double PDF invoices.
458 *
459 * @param int $idorder the ID of the booking
460 *
461 * @return bool
462 */
463 protected function hasAnalogicInvoice($idorder)
464 {
465 if (empty($idorder)) {
466 return false;
467 }
468
469 $this->dbo->setQuery(
470 $this->dbo->getQuery(true)
471 ->select($this->dbo->qn('id'))
472 ->from($this->dbo->qn('#__vikbooking_invoices'))
473 ->where($this->dbo->qn('idorder') . ' = ' . (int)$idorder)
474 );
475
476 if ($this->dbo->loadResult()) {
477 return true;
478 }
479
480 return false;
481 }
482
483 /**
484 * Generates an analogic invoice in PDF format for the given booking ID.
485 *
486 * @param int $idorder the ID of the booking.
487 * @param int $invnum the number of the invoice.
488 * @param string $invdate the date for the invoice in Y-m-d format.
489 *
490 * @return boolean
491 */
492 protected function generateAnalogicInvoice($idorder, $invnum, $invdate)
493 {
494 if (empty($idorder)) {
495 return false;
496 }
497
498 // get booking record
499 $this->dbo->setQuery(
500 $this->dbo->getQuery(true)
501 ->select($this->dbo->qn('o') . '.*')
502 ->select($this->dbo->qn('co.idcustomer'))
503 ->select('CONCAT_WS(\' \', ' . $this->dbo->qn('c.first_name') . ', ' . $this->dbo->qn('c.last_name') . ') AS ' . $this->dbo->qn('customer_name'))
504 ->select([
505 $this->dbo->qn('c.pin', 'customer_pin'),
506 $this->dbo->qn('nat.country_name'),
507 ])
508 ->from($this->dbo->qn('#__vikbooking_orders', 'o'))
509 ->leftJoin($this->dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $this->dbo->qn('co.idorder') . ' = ' . $this->dbo->qn('o.id'))
510 ->leftJoin($this->dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $this->dbo->qn('c.id') . ' = ' . $this->dbo->qn('co.idcustomer'))
511 ->leftJoin($this->dbo->qn('#__vikbooking_countries', 'nat') . ' ON ' . $this->dbo->qn('nat.country_3_code') . ' = ' . $this->dbo->qn('o.country'))
512 ->where($this->dbo->qn('o.id') . ' = ' . (int)$idorder)
513 ->where($this->dbo->qn('o.status') . ' = ' . $this->dbo->q('confirmed'))
514 ->where($this->dbo->qn('o.total') . ' > 0')
515 );
516
517 $booking = $this->dbo->loadAssoc();
518
519 if (!$booking) {
520 return false;
521 }
522
523 try {
524 $res = VikBooking::generateBookingInvoice($booking, $invnum, '', date($this->getDateFormat(), strtotime($invdate)));
525 } catch (Exception $e) {
526 return false;
527 }
528
529 return $res;
530 }
531
532 /**
533 * Loads an helper file to obtain HTML content within a buffer.
534 *
535 * @param string the path to the layout/helper file to include
536 * @param array the vars needed by the layout/helper file
537 *
538 * @return string the HTML content to print, or an empty string
539 */
540 protected function loadHelperFile($fpath, $data = array())
541 {
542 if (!is_file($fpath)) {
543 return '';
544 }
545
546 // capture the content of the layout/helper file within a buffer
547 ob_start();
548 include $fpath;
549 $content = ob_get_contents();
550 ob_end_clean();
551
552 // return the content to be displayed
553 return $content;
554 }
555
556 /**
557 * Requires an helper file.
558 *
559 * @return void
560 */
561 protected function importHelper($fpath)
562 {
563 if (!is_file($fpath)) {
564 return;
565 }
566
567 require_once $fpath;
568 }
569
570 /**
571 * Returns whether the driver has some session filters to
572 * immediately call getBookingsData() when the page loads.
573 * Child classes could override this method depending on the needs.
574 *
575 * @return boolean
576 */
577 public function hasFiltersSet()
578 {
579 return false;
580 }
581
582 /**
583 * Loads the jQuery UI Datepicker.
584 * Method used only by sub-classes.
585 *
586 * @return self
587 */
588 protected function loadDatePicker()
589 {
590 $vbo_app = VikBooking::getVboApplication();
591 $vbo_app->loadDatePicker();
592
593 return $this;
594 }
595
596 /**
597 * Loads all the rooms in VBO and returns the array.
598 *
599 * @return array associative array with key=ID value=data
600 */
601 protected function getRooms()
602 {
603 $rooms = [];
604
605 $q = "SELECT * FROM `#__vikbooking_rooms` ORDER BY `name` ASC;";
606 $this->dbo->setQuery($q);
607 $all = $this->dbo->loadAssocList();
608
609 foreach ($all as $r) {
610 $rooms[$r['id']] = $r;
611 }
612
613 return $rooms;
614 }
615
616 /**
617 * Concatenates the JavaScript rules.
618 * Method used only by sub-classes.
619 *
620 * @param string $str
621 *
622 * @return self
623 */
624 protected function setScript($str)
625 {
626 $this->driverScript .= $str."\n";
627
628 return $this;
629 }
630
631 /**
632 * Returns the aliquote number from the given record ID.
633 *
634 * @param int $idvat the ID of the IVA record
635 *
636 * @return float the aliquot found or 0
637 */
638 protected function getAliquoteById($idvat)
639 {
640 $aliq = 0;
641
642 if (!empty($idvat)) {
643 $q = "SELECT `aliq` FROM `#__vikbooking_iva` WHERE `id`=" . (int)$idvat . ";";
644 $this->dbo->setQuery($q);
645 $aliq = $this->dbo->loadResult();
646
647 if (!$aliq) {
648 return 0;
649 }
650 }
651
652 return floatval($aliq);
653 }
654
655 /**
656 * Returns the aliquote number from the given price ID.
657 *
658 * @param int $idvat the ID of the IVA record
659 *
660 * @return float the aliquot found or 0
661 */
662 protected function getAliquoteFromPriceId($idprice)
663 {
664 $aliq = 0;
665
666 if (!empty($idprice)) {
667 $q = "SELECT `p`.`idiva`,`i`.`aliq` FROM `#__vikbooking_prices` AS `p` LEFT JOIN `#__vikbooking_iva` `i` ON `i`.`id`=`p`.`idiva` WHERE `p`.`id`=".(int)$idprice.";";
668 $this->dbo->setQuery($q);
669 $data = $this->dbo->loadAssoc();
670 if ($data) {
671 $aliq = $data['aliq'];
672 }
673 }
674
675 return floatval($aliq);
676 }
677
678 /**
679 * Updates the current invoice number for later use.
680 *
681 * @param int $invnum the invoice number to set
682 *
683 * @return void
684 */
685 protected function updateInvoiceNumber($invnum)
686 {
687 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$this->dbo->quote((int)$invnum)." WHERE `param`='invoiceinum';";
688 $this->dbo->setQuery($q);
689 $this->dbo->execute();
690 }
691
692 /**
693 * Updates the progrssive number for the data transmission for this driver.
694 *
695 * @param int $num the prograssive number to set (should be already increased)
696 *
697 * @return void
698 */
699 protected function updateProgressiveNumber($num)
700 {
701 $q = "UPDATE `#__vikbooking_einvoicing_config` SET `progcount`=".(int)$num." WHERE `driver`=".$this->dbo->quote($this->getFileName()).";";
702 $this->dbo->setQuery($q);
703 $this->dbo->execute();
704 }
705
706 /**
707 * Attempts to format the given XML string through DOMDocument.
708 * This method takes the XML string by reference.
709 *
710 * @param string $xml the XML string to be formatted
711 *
712 * @return string the formatted string or the original string
713 */
714 protected function formatXmlString(&$xml)
715 {
716 if (!class_exists('DOMDocument')) {
717 // we cannot format the XML because DOMDocument is missing
718 return $xml;
719 }
720
721 $dom = new DOMDocument;
722 $dom->preserveWhiteSpace = false;
723 $dom->loadXML($xml);
724 $dom->formatOutput = true;
725 $xml = $dom->saveXML();
726
727 return $xml;
728 }
729
730 /**
731 * Returns whether the debug request has been set.
732 *
733 * @return boolean
734 */
735 protected function debugging()
736 {
737 $debug = VikRequest::getInt('e4j_debug', 0, 'request');
738
739 return $debug;
740 }
741
742 /**
743 * Gets the current script string.
744 *
745 * @return string
746 */
747 public function getScript()
748 {
749 return rtrim($this->driverScript, "\n");
750 }
751
752 /**
753 * Returns the date format in VBO for date, jQuery UI, Joomla.
754 * Method used only by sub-classes.
755 *
756 * @param string $type
757 *
758 * @return string
759 */
760 protected function getDateFormat($type = 'date')
761 {
762 $nowdf = VikBooking::getDateFormat();
763 if ($nowdf == "%d/%m/%Y") {
764 $df = 'd/m/Y';
765 $juidf = 'dd/mm/yy';
766 } elseif ($nowdf == "%m/%d/%Y") {
767 $df = 'm/d/Y';
768 $juidf = 'mm/dd/yy';
769 } else {
770 $df = 'Y/m/d';
771 $juidf = 'yy/mm/dd';
772 }
773
774 switch ($type) {
775 case 'jui':
776 return $juidf;
777 case 'joomla':
778 return $nowdf;
779 default:
780 return $df;
781 }
782 }
783
784 /**
785 * Returns the translated weekday.
786 * Uses the back-end language definitions.
787 *
788 * @param int $wday
789 * @param string $type use 'long' for the full name of the week, short for the 3-char version
790 *
791 * @return string
792 */
793 protected function getWdayString($wday, $type = 'long')
794 {
795 $wdays_map_long = array(
796 JText::translate('VBWEEKDAYZERO'),
797 JText::translate('VBWEEKDAYONE'),
798 JText::translate('VBWEEKDAYTWO'),
799 JText::translate('VBWEEKDAYTHREE'),
800 JText::translate('VBWEEKDAYFOUR'),
801 JText::translate('VBWEEKDAYFIVE'),
802 JText::translate('VBWEEKDAYSIX')
803 );
804
805 $wdays_map_short = array(
806 JText::translate('VBSUN'),
807 JText::translate('VBMON'),
808 JText::translate('VBTUE'),
809 JText::translate('VBWED'),
810 JText::translate('VBTHU'),
811 JText::translate('VBFRI'),
812 JText::translate('VBSAT')
813 );
814
815 if ($type != 'long') {
816 return isset($wdays_map_short[(int)$wday]) ? $wdays_map_short[(int)$wday] : '';
817 }
818
819 return isset($wdays_map_long[(int)$wday]) ? $wdays_map_long[(int)$wday] : '';
820 }
821
822 /**
823 * Returns the translated month.
824 * Uses the back-end language definitions.
825 *
826 * @param int $month the month to convert (from 1 to 12)
827 *
828 * @return string
829 */
830 protected function getMonthString($mon)
831 {
832 $mon--;
833
834 $months_map_long = array(
835 JText::translate('VBMONTHONE'),
836 JText::translate('VBMONTHTWO'),
837 JText::translate('VBMONTHTHREE'),
838 JText::translate('VBMONTHFOUR'),
839 JText::translate('VBMONTHFIVE'),
840 JText::translate('VBMONTHSIX'),
841 JText::translate('VBMONTHSEVEN'),
842 JText::translate('VBMONTHEIGHT'),
843 JText::translate('VBMONTHNINE'),
844 JText::translate('VBMONTHTEN'),
845 JText::translate('VBMONTHELEVEN'),
846 JText::translate('VBMONTHTWELVE')
847 );
848
849 return isset($months_map_long[(int)$mon]) ? $months_map_long[(int)$mon] : '';
850 }
851
852 /**
853 * Replaces accents and special characters to their non-special version.
854 * Takes also rid of the ampersand symbol that would break the XML.
855 *
856 * @param string $string the string to parse
857 *
858 * @return string
859 */
860 protected function convertSpecials($string)
861 {
862 // map of special characters
863 $table = array(
864 'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', '�
865 '=>'A', 'Ă'=>'A', 'Ā'=>'A', 'Ą'=>'A', 'Æ'=>'A', 'Ǽ'=>'A',
866 'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'ă'=>'a', 'ā'=>'a', '�
867 '=>'a', 'æ'=>'a', 'ǽ'=>'a',
868
869 'Þ'=>'B', 'þ'=>'b', 'ß'=>'Ss',
870
871 'Ç'=>'C', 'Č'=>'C', 'Ć'=>'C', 'Ĉ'=>'C', 'Ċ'=>'C',
872 'ç'=>'c', 'č'=>'c', 'ć'=>'c', 'ĉ'=>'c', 'ċ'=>'c',
873
874 'Đ'=>'Dj', 'Ď'=>'D',
875 'đ'=>'dj', 'ď'=>'d',
876
877 'È'=>'E', 'É'=>'E', 'Ê'=>'E', 'Ë'=>'E', 'Ĕ'=>'E', 'Ē'=>'E', 'Ę'=>'E', 'Ė'=>'E',
878 'è'=>'e', 'é'=>'e', 'ê'=>'e', 'ë'=>'e', 'ĕ'=>'e', 'ē'=>'e', 'ę'=>'e', 'ė'=>'e',
879
880 'Ĝ'=>'G', 'Ğ'=>'G', 'Ġ'=>'G', 'Ģ'=>'G',
881 'ĝ'=>'g', 'ğ'=>'g', 'ġ'=>'g', 'ģ'=>'g',
882
883 'Ĥ'=>'H', 'Ħ'=>'H',
884 'ĥ'=>'h', 'ħ'=>'h',
885
886 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'İ'=>'I', 'Ĩ'=>'I', 'Ī'=>'I', 'Ĭ'=>'I', 'Į'=>'I',
887 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'į'=>'i', 'ĩ'=>'i', 'ī'=>'i', 'ĭ'=>'i', 'ı'=>'i',
888
889 'Ĵ'=>'J',
890 'ĵ'=>'j',
891
892 'Ķ'=>'K',
893 'ķ'=>'k', 'ĸ'=>'k',
894
895 'Ĺ'=>'L', 'Ļ'=>'L', 'Ľ'=>'L', 'Ŀ'=>'L', 'Ł'=>'L',
896 'ĺ'=>'l', 'ļ'=>'l', 'ľ'=>'l', 'ŀ'=>'l', 'ł'=>'l',
897
898 'Ñ'=>'N', 'Ń'=>'N', 'Ň'=>'N', '�
899 '=>'N', 'Ŋ'=>'N',
900 'ñ'=>'n', 'ń'=>'n', 'ň'=>'n', 'ņ'=>'n', 'ŋ'=>'n', 'ʼn'=>'n',
901
902 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O', 'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ō'=>'O', 'Ŏ'=>'O', 'Ő'=>'O', 'Œ'=>'O',
903 'ò'=>'o', 'ó'=>'o', 'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ō'=>'o', 'ŏ'=>'o', 'ő'=>'o', 'œ'=>'o', 'ð'=>'o',
904
905 'Ŕ'=>'R', 'Ř'=>'R',
906 'ŕ'=>'r', 'ř'=>'r', 'ŗ'=>'r',
907
908 'Š'=>'S', 'Ŝ'=>'S', 'Ś'=>'S', 'Ş'=>'S',
909 'š'=>'s', 'ŝ'=>'s', 'ś'=>'s', 'ş'=>'s',
910
911 'Ŧ'=>'T', 'Ţ'=>'T', 'Ť'=>'T',
912 'ŧ'=>'t', 'ţ'=>'t', 'ť'=>'t',
913
914 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ũ'=>'U', 'Ū'=>'U', 'Ŭ'=>'U', 'Ů'=>'U', 'Ű'=>'U', 'Ų'=>'U',
915 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ü'=>'u', 'ũ'=>'u', 'ū'=>'u', 'ŭ'=>'u', 'ů'=>'u', 'ű'=>'u', 'ų'=>'u',
916
917 'Ŵ'=>'W', 'Ẁ'=>'W', 'Ẃ'=>'W', 'Ẅ'=>'W',
918 'ŵ'=>'w', 'ẁ'=>'w', 'ẃ'=>'w', '�
919 '=>'w',
920
921 'Ý'=>'Y', 'Ÿ'=>'Y', 'Ŷ'=>'Y',
922 'ý'=>'y', 'ÿ'=>'y', 'ŷ'=>'y',
923
924 'Ž'=>'Z', 'Ź'=>'Z', 'Ż'=>'Z',
925 'ž'=>'z', 'ź'=>'z', 'ż'=>'z',
926 '+'=>'',
927 );
928
929 $string = strtr($string, $table);
930
931 $string = preg_replace("/[^\x9\xA\xD\x20-\x7F]/u", "", $string);
932
933 // replace ampersand
934 $string = str_replace('&', 'and', $string);
935 // convert to HTML entities for a safe XML content
936 $string = htmlentities($string);
937 // remove any ampersand added by htmlentities()
938 $string = str_replace('&', '', $string);
939
940 return $string;
941 }
942
943 /**
944 * Sets the columns for this driver.
945 *
946 * @param array $arr
947 *
948 * @return self
949 */
950 protected function setDriverCols($arr)
951 {
952 $this->cols = $arr;
953
954 return $this;
955 }
956
957 /**
958 * Returns the columns for this driver.
959 * Should be called after getBookingsData()
960 * or the returned array will be empty.
961 *
962 * @return array
963 */
964 public function getDriverCols()
965 {
966 return $this->cols;
967 }
968
969 /**
970 * Sorts the rows of the driver by key.
971 *
972 * @param string $krsort the key attribute of the array pairs
973 * @param string $krorder ascending (ASC) or descending (DESC)
974 *
975 * @return void
976 */
977 protected function sortRows($krsort, $krorder)
978 {
979 if (empty($krsort) || !(count($this->rows))) {
980 return;
981 }
982
983 $map = array();
984 foreach ($this->rows as $k => $row) {
985 foreach ($row as $kk => $v) {
986 if (isset($v['key']) && $v['key'] == $krsort) {
987 $map[$k] = $v['value'];
988 }
989 }
990 }
991 if (!(count($map))) {
992 return;
993 }
994
995 if ($krorder == 'ASC') {
996 asort($map);
997 } else {
998 arsort($map);
999 }
1000
1001 $sorted = array();
1002 foreach ($map as $k => $v) {
1003 $sorted[$k] = $this->rows[$k];
1004 }
1005
1006 $this->rows = $sorted;
1007 }
1008
1009 /**
1010 * Sets the rows for this driver.
1011 *
1012 * @param array $arr
1013 *
1014 * @return self
1015 */
1016 protected function setDriverRows($arr)
1017 {
1018 $this->rows = $arr;
1019
1020 return $this;
1021 }
1022
1023 /**
1024 * Returns the rows for this driver.
1025 * Should be called after getBookingsData()
1026 * or the returned array will be empty.
1027 *
1028 * @return array
1029 */
1030 public function getDriverRows()
1031 {
1032 return $this->rows;
1033 }
1034
1035 /**
1036 * Sets the footer row (the totals) for this driver.
1037 *
1038 * @param array $arr
1039 *
1040 * @return self
1041 */
1042 protected function setDriverFooterRow($arr)
1043 {
1044 $this->footerRow = $arr;
1045
1046 return $this;
1047 }
1048
1049 /**
1050 * Returns the footer row for this driver.
1051 * Should be called after getBookingsData()
1052 * or the returned array will be empty.
1053 *
1054 * @return array
1055 */
1056 public function getDriverFooterRow()
1057 {
1058 return $this->footerRow;
1059 }
1060
1061 /**
1062 * Sets warning messages by concatenating the existing ones.
1063 * Method used only by sub-classes.
1064 *
1065 * @param string $str
1066 *
1067 * @return self
1068 */
1069 protected function setWarning($str)
1070 {
1071 $this->warning[] = $str;
1072
1073 return $this;
1074 }
1075
1076 /**
1077 * Gets the current warning string.
1078 *
1079 * @return string
1080 */
1081 public function getWarning()
1082 {
1083 return implode('<br/>', $this->warning);
1084 }
1085
1086 /**
1087 * Sets errors by concatenating the existing ones.
1088 * Method used only by sub-classes.
1089 *
1090 * @param string $str
1091 *
1092 * @return self
1093 */
1094 protected function setError($str)
1095 {
1096 $this->error[] = $str;
1097
1098 return $this;
1099 }
1100
1101 /**
1102 * Gets the current error string.
1103 *
1104 * @return string
1105 */
1106 public function getError()
1107 {
1108 return implode('<br/>', $this->error);
1109 }
1110
1111 /**
1112 * Sets info messages by concatenating the existing ones.
1113 * Method used only by sub-classes.
1114 *
1115 * @param string $str
1116 *
1117 * @return self
1118 */
1119 protected function setInfo($str)
1120 {
1121 $this->info[] = $str;
1122
1123 return $this;
1124 }
1125
1126 /**
1127 * Gets the current info string.
1128 *
1129 * @return string
1130 */
1131 public function getInfo()
1132 {
1133 return implode('<br/>', $this->info);
1134 }
1135 }
1136