PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.6.6
VikBooking Hotel Booking Engine & PMS v1.6.6
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 / site / helpers / translator.php

translator.php in VikBooking Hotel Booking Engine & PMS 1.6.6, at site/helpers/translator.php

813 lines 21.2 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 * Global translator class for VikBooking contents.
15 */
16 class VikBookingTranslator
17 {
18 /**
19 * @var string
20 */
21 public $current_lang;
22
23 /**
24 * @var string
25 */
26 public $default_lang;
27
28 /**
29 * @var int
30 */
31 public $lim = 5;
32
33 /**
34 * @var int
35 */
36 public $lim0 = 0;
37
38 /**
39 * @var string
40 */
41 public $navigation = '';
42
43 /**
44 * @var string
45 */
46 public $error = '';
47
48 /**
49 * @var string
50 */
51 private $xml = '';
52
53 /**
54 * @var array
55 */
56 private $all_langs = [];
57
58 /**
59 * @var object
60 */
61 private $dbo;
62
63 /**
64 * @var string
65 */
66 private $translations_path_file;
67
68 /**
69 * @var array
70 */
71 private $translations_buffer = [];
72
73 /**
74 * @var string
75 *
76 * @since 1.16.6 (J) - 1.6.6 (WP)
77 */
78 private $keysearch = '';
79
80 /**
81 * @var array
82 *
83 * @since 1.16.6 (J) - 1.6.6 (WP)
84 */
85 private $cached_cols = [];
86
87 /**
88 * @var string
89 */
90 public static $force_tolang = null;
91
92 /**
93 * Class constructor.
94 */
95 public function __construct()
96 {
97 $app = JFactory::getApplication();
98
99 $this->current_lang = $this->getCurrentLang();
100 $this->default_lang = $this->getDefaultLang();
101 $this->lim = $app->input->getInt('limit', 5);
102 $this->lim0 = $app->input->getInt('limitstart', 0);
103 $this->dbo = JFactory::getDbo();
104 $this->translations_path_file = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'fields' . DIRECTORY_SEPARATOR . 'translations.xml';
105 }
106
107 /**
108 * Gets the language for the current execution.
109 *
110 * @return string
111 */
112 public function getCurrentLang()
113 {
114 if (!$this->current_lang) {
115 $this->current_lang = JFactory::getLanguage()->getTag();
116 }
117
118 return $this->current_lang;
119 }
120
121 /**
122 * Gets the default language for the requested section.
123 *
124 * @param string $section either site or administrator.
125 *
126 * @return string
127 */
128 public function getDefaultLang($section = 'site')
129 {
130 if (VBOPlatformDetection::isWordPress()) {
131 /**
132 * @wponly import the JComponentHelper class
133 */
134 jimport('joomla.application.component.helper');
135 }
136
137 if (!$this->default_lang && $section == 'site') {
138 $this->default_lang = JComponentHelper::getParams('com_languages')->get($section);
139 }
140
141 if ($section == 'site') {
142 return $this->default_lang;
143 }
144
145 return JComponentHelper::getParams('com_languages')->get($section);
146 }
147
148 /**
149 * Returns a list of translation INI files.
150 *
151 * @return array
152 */
153 public function getIniFiles()
154 {
155 // Keys = Lang Def composed as VBINIEXPL.strtoupper(Key)
156 // Values = Paths to INI Files
157
158 if (VBOPlatformDetection::isWordPress()) {
159 /**
160 * @wponly nothing to return
161 */
162 return [];
163 }
164
165 return [
166 'com_vikbooking_front' => ['path' => JPATH_SITE . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR . 'en-GB' . DIRECTORY_SEPARATOR . 'en-GB.com_vikbooking.ini'],
167 'com_vikbooking_admin' => ['path' => JPATH_ADMINISTRATOR . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR . 'en-GB' . DIRECTORY_SEPARATOR . 'en-GB.com_vikbooking.ini'],
168 'com_vikbooking_admin_sys' => ['path' => JPATH_ADMINISTRATOR . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR . 'en-GB' . DIRECTORY_SEPARATOR . 'en-GB.com_vikbooking.sys.ini'],
169 'mod_vikbooking_search' => ['path' => JPATH_SITE . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR . 'en-GB' . DIRECTORY_SEPARATOR . 'en-GB.mod_vikbooking_search.ini'],
170 'mod_vikbooking_horizontalsearch' => ['path' => JPATH_SITE . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR . 'en-GB' . DIRECTORY_SEPARATOR . 'en-GB.mod_vikbooking_horizontalsearch.ini'],
171 ];
172 }
173
174 /**
175 * Builds a list of known languages.
176 *
177 * @return array
178 */
179 public function getLanguagesList()
180 {
181 $langs = [];
182 $known_langs = VikBooking::getVboApplication()->getKnownLanguages();
183
184 foreach ($known_langs as $ltag => $ldet) {
185 if ($ltag == $this->default_lang) {
186 $langs = [$ltag => $ldet] + $langs;
187 } else {
188 $langs[$ltag] = $ldet;
189 }
190 }
191
192 $this->all_langs = $langs;
193
194 return $this->all_langs;
195 }
196
197 /**
198 * Returns a list of known language tags.
199 *
200 * @return array
201 */
202 public function getLanguagesTags()
203 {
204 return array_keys($this->all_langs);
205 }
206
207 /**
208 * Replaces the prefix from a given table name.
209 *
210 * @param string $str the prefixed name.
211 *
212 * @return string
213 */
214 public function replacePrefix($str)
215 {
216 return $this->dbo->replacePrefix($str);
217 }
218
219 /**
220 * Helper method that makes sure the table name starts with the prefix placeholder.
221 * In order to avoid issues with queries containing the prefix placeholder ("#__"),
222 * the name of the table for the translated record is removed from the placeholder prefix.
223 *
224 * @param string $table_name the table name to adjust.
225 *
226 * @return string the adjust table name with the prefix placeholder.
227 *
228 * @since 1.16.0 (J) - 1.6.0 (WP)
229 */
230 public function adjustTablePrefix($table_name)
231 {
232 if (empty($table_name) || !is_string($table_name)) {
233 // nothing to do with the given value
234 return $table_name;
235 }
236
237 if (preg_match("/^#__/", $table_name)) {
238 // default prefix placeholder found at the beginning of the string
239 return $table_name;
240 }
241
242 if (strpos($table_name, 'vikbooking_') === false) {
243 // nothing to do with this table name
244 return $table_name;
245 }
246
247 // make the table name start with the prefix placeholder
248 $table_nm_parts = explode('vikbooking_', $table_name);
249 $table_nm_parts[0] = '#__';
250
251 return implode('vikbooking_', $table_nm_parts);
252 }
253
254 /**
255 * Fixer method for BC. The old structure was storing the table names for
256 * the translated records without the default prefix placeholder ('#__'),
257 * but this is invalid for the backup features. This method converts all
258 * table names for the translated records so that they will contain the
259 * default prefix placeholder at the beginning of the string.
260 *
261 * @return boolean
262 *
263 * @since 1.16.0 (J) - 1.6.0 (WP)
264 */
265 public function normalizeTnTableNames()
266 {
267 $query = $this->dbo->getQuery(true);
268
269 $query->select($this->dbo->qn('t.id'));
270 $query->select($this->dbo->qn('t.table'));
271
272 $query->from($this->dbo->qn('#__vikbooking_translations', 't'));
273
274 $this->dbo->setQuery($query);
275 $translations = $this->dbo->loadObjectList();
276
277 if (!$translations) {
278 // no translation records found
279 return false;
280 }
281
282 foreach ($translations as $tn_record) {
283 // normalize table name with prefix
284 $tn_record->table = $this->adjustTablePrefix($tn_record->table);
285
286 // update record on db
287 $this->dbo->updateObject('#__vikbooking_translations', $tn_record, 'id');
288 }
289
290 return true;
291 }
292
293 /**
294 * Builds an associative list of translation tables.
295 *
296 * @return mixed array or false.
297 */
298 public function getTranslationTables()
299 {
300 $xml = $this->getTranslationsXML();
301 if ($xml === false) {
302 return false;
303 }
304 $tables = [];
305 foreach ($xml->Translation as $translation) {
306 $attr = $translation->attributes();
307 $tables[(string)$attr->table] = JText::translate((string)$attr->name);
308 }
309
310 return $tables;
311 }
312
313 /**
314 * Returns the translated name of the table given the prefix
315 *
316 * @param string $table
317 */
318 public function getTranslationTableName($table)
319 {
320 $xml = $this->getTranslationsXML();
321 $table_name = '';
322 foreach ($xml->Translation as $translation) {
323 $attr = $translation->attributes();
324 if ((string)$attr->table == $table) {
325 return JText::translate((string)$attr->name);
326 }
327 }
328 return $table_name;
329 }
330
331 /**
332 * Returns an array with the XML Columns of the given table
333 *
334 * @param string $table
335 *
336 * @return array
337 */
338 public function getTableColumns($table)
339 {
340 if (isset($this->cached_cols[$table])) {
341 return $this->cached_cols[$table];
342 }
343
344 $xml = $this->getTranslationsXML();
345 $cols = [];
346 foreach ($xml->Translation as $translation) {
347 $attr = $translation->attributes();
348 if ((string)$attr->table == $table) {
349 foreach ($translation->Column as $column) {
350 $col_attr = $column->attributes();
351 if (!property_exists($col_attr, 'name')) {
352 continue;
353 }
354 $ind = (string)$col_attr->name;
355 $cols[$ind]['jlang'] = JText::translate((string)$column);
356 foreach ($col_attr as $key => $val) {
357 $cols[$ind][(string)$key] = (string)$val;
358 }
359 }
360 }
361 }
362
363 // cache table columns
364 $this->cached_cols[$table] = $cols;
365
366 return $cols;
367 }
368
369 /**
370 * Gets the name of the reference column for the given table.
371 *
372 * @param string $table the name of the table.
373 *
374 * @return string
375 *
376 * @since 1.16.6 (J) - 1.6.6 (WP)
377 */
378 public function getTableReferenceColumn($table)
379 {
380 foreach ($this->getTableColumns($table) as $col_name => $col_values) {
381 if (isset($col_values['reference'])) {
382 return $col_name;
383 }
384 }
385
386 return '';
387 }
388
389 /**
390 * Returns the db column marked as reference, of the record. Ex. the name of the Room in this record
391 *
392 * @param array $cols
393 * @param array $record
394 */
395 public function getRecordReferenceName($cols, $record)
396 {
397 foreach ($cols as $key => $values) {
398 if (array_key_exists('reference', $values)) {
399 if (array_key_exists($key, $record)) {
400 return $record[$key];
401 }
402 }
403 }
404 // if not found, not present or empty, return first value of the record
405 return $record[key($record)];
406 }
407
408 /**
409 * Returns the current records for the default language and this table
410 *
411 * @param string $table the name of the table.
412 * @param array $cols array containing the db fields to fetch,
413 * result of array_keys($this->getTableColumns()).
414 *
415 * @return array
416 */
417 public function getTableDefaultDbValues($table, $cols = [])
418 {
419 $def_vals = [];
420
421 if (!$cols) {
422 $cols = array_keys($this->getTableColumns($table));
423 if (!$cols) {
424 $this->setError("Table $table has no Columns.");
425 }
426 }
427
428 if ($cols) {
429 $cols = array_map([$this->dbo, 'qn'], $cols);
430
431 $reference_column = $this->getTableReferenceColumn($table);
432
433 $q = $this->dbo->getQuery(true)
434 ->select('SQL_CALC_FOUND_ROWS ' . $this->dbo->qn('id'))
435 ->select($cols)
436 ->from($this->dbo->qn($table))
437 ->order($this->dbo->qn(($reference_column ?: 'id')) . ' ASC');
438
439 if (!empty($this->keysearch) && $reference_column) {
440 $q->where($this->dbo->qn($reference_column) . ' LIKE ' . $this->dbo->q("%{$this->keysearch}%"));
441 }
442
443 $this->dbo->setQuery($q, $this->lim0, $this->lim);
444 $records = $this->dbo->loadAssocList();
445
446 if ($records) {
447 $this->dbo->setQuery('SELECT FOUND_ROWS();');
448 $this->setPagination($this->dbo->loadResult());
449 foreach ($records as $record) {
450 $ref_id = $record['id'];
451 unset($record['id']);
452 $def_vals[$ref_id] = $record;
453 }
454 } else {
455 $this->setError("Table " . $this->getTranslationTableName($table) . " has no Records.");
456 }
457 }
458
459 return $def_vals;
460 }
461
462 /**
463 * Sets the pagination HTML value for the current
464 * translation list, by using the Joomla native functions.
465 *
466 * @param int $tot_rows
467 */
468 private function setPagination($tot_rows)
469 {
470 jimport('joomla.html.pagination');
471 $pageNav = new JPagination($tot_rows, $this->lim0, $this->lim);
472 $this->navigation = $pageNav->getListFooter();
473 }
474
475 /**
476 * Returns the current pagination HTML value.
477 */
478 public function getPagination()
479 {
480 return $this->navigation;
481 }
482
483 /**
484 * Returns the translated records for this table and language
485 *
486 * @param string $table
487 * @param string $lang
488 *
489 * @return array
490 */
491 public function getTranslatedTable($table, $lang)
492 {
493 $translated = [];
494
495 $q = $this->dbo->getQuery(true);
496
497 $q->select($this->dbo->qn('t') . '.*')
498 ->from($this->dbo->qn('#__vikbooking_translations', 't'))
499 ->where($this->dbo->qn('t.table') . ' = ' . $this->dbo->q($this->adjustTablePrefix($table)))
500 ->where($this->dbo->qn('t.lang') . ' = ' . $this->dbo->q($lang))
501 ->order($this->dbo->qn('t.reference_id') . ' ASC');
502
503 $this->dbo->setQuery($q);
504
505 foreach ($this->dbo->loadAssocList() as $record) {
506 $record['content'] = json_decode($record['content'], true);
507 $translated[$record['reference_id']] = $record;
508 }
509
510 return $translated;
511 }
512
513 /**
514 * Main function to translate contents saved in the database.
515 *
516 * @param array $content The array taken from the database with the default values to be translated, passed as reference.
517 * @param string $table The name of the table containing the translation values, should match with the XML table name.
518 * @param array $alias_keys Key-Value pairs where Key is the ALIAS used and Value is the original field name. Opposite instead for the ID (reference_id).
519 * The key 'id' is always treated differently than the other keys. Correct usage: array('id' => 'idroom', 'room_name' => 'name')
520 * @param array $ids The reference_IDs to be translated, the IDs of the records. Taken from the content array if empty array passed.
521 * @param string $lang Force the translation to a specific language tag like it-IT.
522 *
523 * @return array The initial array with translated values (if applicable).
524 */
525 public function translateContents(&$content, $table, $alias_keys = array(), $ids = array(), $lang = null)
526 {
527 $to_lang = is_null($lang) ? $this->current_lang : $lang;
528 $to_lang = !is_null(self::$force_tolang) ? self::$force_tolang : $to_lang;
529
530 // multilang may be disabled
531 if (!$this->allowMultiLanguage()) {
532 return $content;
533 }
534
535 // check that requested lang is not the default lang
536 if ($to_lang == $this->default_lang || !$content) {
537 return $content;
538 }
539
540 // get all translatable columns of this table
541 $cols = $this->getTableColumns($table);
542
543 // get the reference ids to be translated
544 if (!count($ids)) {
545 $ids = $this->getReferencesFromContents($content, $alias_keys);
546 }
547
548 // load translations buffer for this table or set the var to an empty array
549 $translated = $this->getTranslationsBuffer($table, $ids, $to_lang);
550
551 if (!count($translated)) {
552 // load translations from db
553 $q = "SELECT * FROM `#__vikbooking_translations` WHERE `table`=" . $this->dbo->quote($this->adjustTablePrefix($table)) . " AND `lang`=" . $this->dbo->quote($to_lang) . (count($ids) ? " AND `reference_id` IN (" . implode(",", $ids) . ")" : "") . ";";
554 $this->dbo->setQuery($q);
555 $records = $this->dbo->loadAssocList();
556 foreach ($records as $record) {
557 $record['content'] = json_decode($record['content'], true);
558 if (is_array($record['content']) && $record['content']) {
559 $translated[$record['reference_id']] = $record['content'];
560 }
561 }
562 }
563
564 if (count($translated)) {
565 // set translations buffer
566 $this->translations_buffer[$table][$to_lang] = $translated;
567
568 // fetch reference_id to be translated and replace default lang values
569 $reference_key = array_key_exists('id', $alias_keys) ? $alias_keys['id'] : 'id';
570 foreach ($content as $ckey => $cvals) {
571 $reference_id = 0;
572 if (is_array($cvals)) {
573 foreach ($cvals as $subckey => $subcvals) {
574 if ($subckey == $reference_key) {
575 $reference_id = (int)$subcvals;
576 break;
577 }
578 }
579 $content[$ckey] = $this->translateArrayValues($cvals, $cols, $reference_id, $alias_keys, $translated);
580 } elseif ($ckey == $reference_key) {
581 $reference_id = (int)$cvals;
582 $content = $this->translateArrayValues($content, $cols, $reference_id, $alias_keys, $translated);
583 break;
584 }
585 }
586 }
587
588 return $content;
589 }
590
591 /**
592 * Compares the array to be translated with the translation and replaces the array values if not empty.
593 *
594 * @param array $content default lang values to be translated.
595 * @param array $alias_keys Key_Values pairs where Key is the ALIAS used and Value is the original
596 * field name. Opposite instead for the ID (reference_id).
597 */
598 private function getReferencesFromContents($content, $alias_keys)
599 {
600 $references = array();
601 $reference_key = array_key_exists('id', $alias_keys) ? $alias_keys['id'] : 'id';
602 foreach ($content as $ckey => $cvals) {
603 if (is_array($cvals)) {
604 foreach ($cvals as $subckey => $subcvals) {
605 if ($subckey == $reference_key) {
606 $references[] = (int)$subcvals;
607 break;
608 }
609 }
610 } elseif ($ckey == $reference_key) {
611 $references[] = (int)$cvals;
612 break;
613 }
614 }
615 if (count($references) > 0) {
616 $references = array_unique($references);
617 }
618
619 return $references;
620 }
621
622 /**
623 * Check whether these reference IDs were already fetched from the db for this table
624 *
625 * @param string $table
626 * @param array $ids
627 * @param string $lang
628 *
629 * @return array
630 */
631 private function getTranslationsBuffer($table, $ids, $lang)
632 {
633 if (!count($this->translations_buffer) || !isset($this->translations_buffer[$table]) || !isset($this->translations_buffer[$table][$lang])) {
634 return array();
635 }
636
637 $missing = false;
638 foreach ($ids as $id) {
639 if (!isset($this->translations_buffer[$table][$lang][$id])) {
640 $missing = true;
641 break;
642 }
643 }
644
645 return $missing === false ? $this->translations_buffer[$table][$lang] : array();
646 }
647
648 /**
649 * Compares the array to be translated with the translation and replaces the array values if not empty.
650 *
651 * @param array $content default lang values to be translated.
652 * @param array $cols the columns of this table.
653 * @param int $reference_id reference_id.
654 * @param array $alias_keys Key_Values pairs where Key is the ALIAS used and Value is the original field name.
655 * Opposite instead for the ID (reference_id).
656 * @param array $translated translated.
657 */
658 private function translateArrayValues($content, $cols, $reference_id, $alias_keys, $translated)
659 {
660 if (empty($reference_id)) {
661 return $content;
662 }
663
664 if (!array_key_exists($reference_id, $translated)) {
665 return $content;
666 }
667
668 foreach ($content as $key => $value) {
669 $native_key = $key;
670 if (count($alias_keys) > 0 && array_key_exists($key, $alias_keys) && $key != 'id') {
671 $key = $alias_keys[$key];
672 }
673 if (!array_key_exists($key, $cols)) {
674 continue;
675 }
676 if (array_key_exists($key, $translated[$reference_id]) && strlen($translated[$reference_id][$key]) > 0) {
677 $type = $cols[$key]['type'];
678 if ($type == 'json') {
679 // only the translated and not empty keys will be taken from the translation
680 $tn_json = json_decode($translated[$reference_id][$key], true);
681 $content_json = json_decode($value, true);
682 $jkeys = !empty($cols[$key]['keys']) ? explode(',', $cols[$key]['keys']) : array();
683 if (is_array($tn_json) && $tn_json && is_array($content_json) && $content_json) {
684 foreach ($content_json as $jk => $jv) {
685 if (array_key_exists($jk, $tn_json) && strlen($tn_json[$jk]) > 0) {
686 $content_json[$jk] = $tn_json[$jk];
687 }
688 }
689 $content[$native_key] = json_encode($content_json);
690 }
691 } else {
692 // field is a text type or a text-derived one
693 $content[$native_key] = $translated[$reference_id][$key];
694 }
695 }
696 }
697
698 return $content;
699 }
700
701 /**
702 * Sets and Returns the SimpleXML object for the translations
703 */
704 public function getTranslationsXML()
705 {
706 if (!is_file($this->translations_path_file)) {
707 $this->setError($this->translations_path_file . ' does not exist or is not readable');
708 return false;
709 }
710 if (!function_exists('simplexml_load_file')) {
711 $this->setError('Function simplexml_load_file is not available on the server.');
712 return false;
713 }
714 if (is_object($this->xml)) {
715 return $this->xml;
716 }
717 libxml_use_internal_errors(true);
718 if (($xml = simplexml_load_file($this->translations_path_file)) === false) {
719 $this->setError("Error reading XML:\n".$this->libxml_display_errors());
720 return false;
721 }
722 $this->xml = $xml;
723 return $xml;
724 }
725
726 /**
727 * Tells if the multi-language environment is enabled.
728 *
729 * @return bool
730 */
731 private function allowMultiLanguage()
732 {
733 return VikBooking::allowMultiLanguage();
734 }
735
736 /**
737 * Explanation of the XML error
738 *
739 * @param $error
740 */
741 public function libxml_display_error($error)
742 {
743 $return = "\n";
744 switch ($error->level) {
745 case LIBXML_ERR_WARNING :
746 $return .= "Warning ".$error->code.": ";
747 break;
748 case LIBXML_ERR_ERROR :
749 $return .= "Error ".$error->code.": ";
750 break;
751 case LIBXML_ERR_FATAL :
752 $return .= "Fatal Error ".$error->code.": ";
753 break;
754 }
755 $return .= trim($error->message);
756 if ($error->file) {
757 $return .= " in ".$error->file;
758 }
759 $return .= " on line ".$error->line."\n";
760 return $return;
761 }
762
763 /**
764 * Get the XML errors occurred
765 */
766 public function libxml_display_errors()
767 {
768 $errorstr = "";
769 $errors = libxml_get_errors();
770 foreach ($errors as $error) {
771 $errorstr .= $this->libxml_display_error($error);
772 }
773 libxml_clear_errors();
774 return $errorstr;
775 }
776
777 /**
778 * Concatenates an error to the errors string.
779 *
780 * @param string $str the error string to add.
781 *
782 * @return void
783 */
784 private function setError($str)
785 {
786 $this->error .= $str."\n";
787 }
788
789 /**
790 * Returns the current error string in HTML format.
791 *
792 * @return string
793 */
794 public function getError()
795 {
796 return nl2br(rtrim($this->error, "\n"));
797 }
798
799 /**
800 * Sets a key value to search/filter translations for.
801 *
802 * @param string $key the term to search/filter.
803 *
804 * @return self
805 */
806 public function setKeySearch($key)
807 {
808 $this->keysearch = (string) $key;
809
810 return $this;
811 }
812 }
813