PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / models / employeesearch.php
vikappointments / site / models Last commit date
allorders.php 1 day ago calendarweek.php 1 day ago cart.php 1 day ago confirmapp.php 1 day ago empaccountstat.php 1 day ago empattachser.php 1 day ago empcoupons.php 1 day ago empcustfields.php 1 day ago empeditcoupon.php 1 day ago empeditcustfield.php 1 day ago empeditlocation.php 1 day ago empeditpay.php 1 day ago empeditprofile.php 1 day ago empeditservice.php 1 day ago empeditwdays.php 1 day ago emplocations.php 1 day ago emplocwdays.php 1 day ago emplogin.php 1 day ago employeesearch.php 1 day ago employeeslist.php 1 day ago empmanres.php 1 day ago emppaylist.php 1 day ago empserviceslist.php 1 day ago empsettingsman.php 1 day ago empsubscrcart.php 1 day ago empsubscrhistory.php 1 day ago empsubscrorder.php 1 day ago empwdays.php 1 day ago index.html 1 day ago packages.php 1 day ago packagescart.php 1 day ago packagesconfirm.php 1 day ago packorders.php 1 day ago servicesearch.php 1 day ago serviceslist.php 1 day ago subscrcart.php 1 day ago subscrhistory.php 1 day ago subscrpayment.php 1 day ago
employeesearch.php
878 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 VAPLoader::import('libraries.mvc.model');
15
16 /**
17 * VikAppointments employee search view model.
18 *
19 * @since 1.7
20 */
21 class VikAppointmentsModelEmployeesearch extends JModelVAP
22 {
23 /**
24 * Returns the employee details.
25 *
26 * @param integer $id The employee ID.
27 * @param array $options An array of options.
28 *
29 * @return object The employee details.
30 */
31 public function getEmployee($id, array $options = array())
32 {
33 $dispatcher = VAPFactory::getEventDispatcher();
34
35 // inject employee ID within options array, which will be
36 // passed to the hooks dispatcher
37 $options['id_employee'] = (int) $id;
38
39 $dbo = JFactory::getDbo();
40
41 // inner query to calculate the average rating of the employee
42 $rating = $dbo->getQuery(true)
43 ->select('AVG(' . $dbo->qn('re.rating') . ')')
44 ->from($dbo->qn('#__vikappointments_reviews', 're'))
45 ->where(array(
46 $dbo->qn('e.id') . ' = ' . $dbo->qn('re.id_employee'),
47 $dbo->qn('re.published') . ' = 1',
48 ));
49
50 $q = $dbo->getQuery(true);
51
52 $q->select('e.*');
53 $q->select('(' . $rating . ') AS ' . $dbo->qn('ratingAVG'));
54 $q->select($dbo->qn('eg.name', 'group_name'));
55 $q->select($dbo->qn('eg.description', 'group_description'));
56
57 $q->from($dbo->qn('#__vikappointments_employee', 'e'));
58 $q->leftjoin($dbo->qn('#__vikappointments_employee_group', 'eg') . ' ON ' . $dbo->qn('e.id_group') . ' = ' . $dbo->qn('eg.id'));
59
60 $q->where($dbo->qn('e.id') . ' = ' . (int) $id);
61
62 /**
63 * Trigger hook to manipulate the query at runtime. Third party plugins
64 * can extend the query by applying further conditions or selecting
65 * additional data.
66 *
67 * @param mixed &$query Either a query builder or a query string.
68 * @param array &$options An array of options.
69 *
70 * @return void
71 *
72 * @since 1.7
73 */
74 $dispatcher->trigger('onBuildEmployeeSearchQuery', array(&$q, &$options));
75
76 $dbo->setQuery($q, 0, 1);
77 $employee = $dbo->loadObject();
78
79 if (!$employee)
80 {
81 // employee not found
82 $this->setError(JText::translate('VAPEMPNOTFOUNDERROR'));
83 return false;
84 }
85
86 // build employee data
87 $employee = $this->buildEmployeeData($employee, $options);
88
89 // get employee model
90 $model = JModelVAP::getInstance('employee');
91
92 // make sure the employee is active
93 if (!$model->isVisible($employee))
94 {
95 // employee not active or not visible
96 $this->setError(JText::translate('VAPEMPNOTFOUNDERROR'));
97 return false;
98 }
99
100 // translate employee details
101 $this->translate($employee);
102
103 /**
104 * Trigger hook to manipulate the query response at runtime. Third party
105 * plugins can include further details into the employee object.
106 *
107 * @param object $employee An object holding the employee details.
108 * @param JModel $model The current model.
109 *
110 * @return void
111 *
112 * @since 1.7
113 */
114 $dispatcher->trigger('onBuildEmployeeSearchData', array($employee, $this));
115
116 return $employee;
117 }
118
119 /**
120 * Returns the list of locations assigned to the given employee.
121 *
122 * @param mixed $id Either the employee ID or an array of IDs.
123 *
124 * @return array The locations list.
125 */
126 public function getLocations($id, $id_service = null)
127 {
128 $locations = array();
129
130 if (!$id)
131 {
132 return $locations;
133 }
134
135 $dbo = JFactory::getDbo();
136
137 $q = $dbo->getQuery(true);
138
139 // load all locations used by the employee
140 $q->select($dbo->qn('l.id'));
141 $q->from($dbo->qn('#__vikappointments_employee_location', 'l'));
142 $q->leftjoin($dbo->qn('#__vikappointments_emp_worktime', 'w') . ' ON ' . $dbo->qn('l.id') . ' = ' . $dbo->qn('w.id_location'));
143
144 if ($id_service)
145 {
146 // filter working days by service
147 $q->where($dbo->qn('w.id_service') . ' = ' . (int) $id_service);
148 }
149
150 if (is_array($id))
151 {
152 // get all working days assigned to the employees
153 $q->where($dbo->qn('w.id_employee') . ' IN (' . implode(',', $id) . ')');
154
155 // then look for global locations or locations created by the
156 // specified employees
157 $q->andWhere(array(
158 $dbo->qn('l.id_employee') . ' <= 0',
159 $dbo->qn('l.id_employee') . ' IN (' . implode(',', $id) . ')',
160 ), 'OR');
161 }
162 else
163 {
164 // get all working days assigned to this employee
165 $q->where($dbo->qn('w.id_employee') . ' = ' . (int) $id);
166
167 // then look for global locations or locations created by the
168 // specified employee
169 $q->andWhere(array(
170 $dbo->qn('l.id_employee') . ' <= 0',
171 $dbo->qn('l.id_employee') . ' = ' . (int) $id,
172 ), 'OR');
173 }
174
175 $q->group($dbo->qn('l.id'));
176
177 $dbo->setQuery($q);
178
179 if ($rows = $dbo->loadColumn())
180 {
181 // create location model only in case of records
182 $model = JModelVAP::getInstance('location');
183
184 // iterate locations found and fetch info one by one
185 foreach ($rows as $id_location)
186 {
187 // get details through the location model
188 $locations[] = $model->getInfo($id_location);
189 }
190 }
191
192 return $locations;
193 }
194
195 /**
196 * Returns an object holding the details of the calendar.
197 *
198 * @param array &$options An array of options.
199 *
200 * @return object
201 */
202 public function getCalendar(&$options)
203 {
204 $config = VAPFactory::getConfig();
205 $dispatcher = VAPFactory::getEventDispatcher();
206
207 // set number of visible months
208 $options['numcal'] = $config->getUint('numcals');
209
210 // get current date at midnight
211 $date = JFactory::getDate('today 00:00:00', VikAppointments::getUserTimezone());
212 // Back to the first day of the month.
213 // Do not use "first day of" modifier because PHP 7.3
214 // seems to experience some strange behaviors.
215 $date->modify($date->format('Y-m-01', true));
216
217 // check whether an initial date was set and it is in the future
218 if (empty($options['date']) || $options['date'] < $date->format('Y-m-d', $local = true))
219 {
220 // get initial month/year from configuration
221 $month = $config->getUint('calsfrom');
222 $year = $config->getUint('calsfromyear');
223
224 // make sure the selected month and year are not in the past
225 if (!$month || !$year || JFactory::getDate("{$year}-{$month}-01") < $date->format('Y-m-d', $local = true))
226 {
227 // use current month and year
228 $month = (int) $date->format('n', $local = true);
229 $year = (int) $date->format('Y', $local = true);
230 }
231
232 // set initial date
233 $options['date'] = JFactory::getDate("{$year}-{$month}-01")->format('Y-m-d');
234 }
235
236 // get service details
237 $assocModel = JModelVAP::getInstance('serempassoc');
238 $service = $assocModel->getOverrides($options['id_ser'], $options['id_emp']);
239
240 if ($service && !VAPDateHelper::isNull($service->start_publishing))
241 {
242 // fetch employee/system timezone
243 $emp_tz = JModelVAP::getInstance('employee')->getTimezone($options['id_emp']);
244
245 // adjust start publishing to given timezone
246 $startPub = JFactory::getDate($service->start_publishing);
247 $startPub->setTimezone(new DateTimeZone($emp_tz));
248
249 // compare service start publishing with start date
250 if ($startPub->format('Y-m-01', $local = true) > $options['date'])
251 {
252 // use service start publishing
253 $options['date'] = $startPub->format('Y-m-01', $local = true);
254 }
255 }
256
257 if (empty($options['start']) || $options['start'] < $options['date'])
258 {
259 // use the default initial date
260 $options['start'] = $options['date'];
261 }
262
263 // fetch calendar layout according to the configuration of the program
264 $options['layout'] = $config->get('calendarlayoutsite', 'monthly');
265
266 /**
267 * Trigger hook before fetching the data needed to display a calendar.
268 * Useful, in example, to switch the calendar layout at runtime by altering
269 * the related array attribute.
270 *
271 * @param array &$options An array of calendar options.
272 *
273 * @return void
274 *
275 * @since 1.7
276 */
277 $dispatcher->trigger('onBeforeFetchCalendarData', array(&$options));
278
279 if (empty($options['layout']))
280 {
281 $options['layout'] = 'monthly';
282 }
283
284 // flag search as performed by the customer
285 $options['admin'] = false;
286
287 if ($options['layout'] == 'weekly')
288 {
289 // load apposite front-end model
290 $calModelName = 'calendarweek';
291 }
292 else
293 {
294 // load default model used in the back-end too
295 $calModelName = 'calendar';
296 }
297
298 // obtain calendar data through back-end model
299 $calendar = JModelVAP::getInstance($calModelName);
300
301 // make sure the requested model exists and owns a valid callback
302 if ($calendar && method_exists($calendar, 'getCalendar'))
303 {
304 $data = $calendar->getCalendar($options);
305 }
306 else
307 {
308 // use an empty object, which will be probably filled by
309 // the "after fetch" hook
310 $data = new stdClass;
311 }
312
313 $data->layout = $options['layout'];
314
315 // include months select options
316 $data->select = array();
317
318 $dt = JFactory::getDate($options['date']);
319
320 for ($i = 0; $i < $config->getUint('nummonths', 0); $i++)
321 {
322 // get date string
323 $k = $dt->format('Y-m-01');
324 // register select option
325 $data->select[$k] = $dt->monthToString($dt->format('n'));
326 // go to next month
327 $dt->modify('+1 month');
328 }
329
330 /**
331 * Trigger hook after fetching the data needed to display a calendar.
332 * Useful, in example, to include additional information about the calendar
333 * or to implement a new layout type.
334 *
335 * @param object $data The object holding the calendar data.
336 * @param array $options An array of calendar options.
337 *
338 * @return void
339 *
340 * @since 1.7
341 */
342 $dispatcher->trigger('onAfterFetchCalendarData', array($data, $options));
343
344 return $data;
345 }
346
347 /**
348 * Calculates the resulting availability timeline according
349 * to the specified search options.
350 *
351 * @param array $options An array of options.
352 *
353 * @return mixed The resulting renderer.
354 */
355 public function getTimeline($options)
356 {
357 $config = VAPFactory::getConfig();
358
359 /**
360 * Validate service restrictions.
361 *
362 * @since 1.6.5
363 * @since 1.7.7 Check whether the timeline should be displayed anyway.
364 */
365 if (empty($options['admin']) && !$config->getBool('restrtimeline'))
366 {
367 VAPLoader::import('libraries.models.restrictions');
368
369 if (!VAPSpecialRestrictions::canBookService($options['id_ser'], $options['date'], $restr))
370 {
371 if (VikAppointments::isUserLogged())
372 {
373 // the user already reached the maximum threshold
374 $err = JText::sprintf(
375 'VAPRESTRICTIONLIMITREACHED',
376 $restr->maxapp,
377 strtolower(JText::translate('VAPMANAGERESTRINTERVAL' . strtoupper($restr->interval)))
378 );
379 }
380 else
381 {
382 // login needed before to see the available slots
383 $err = JText::translate('VAPRESTRICTIONLIMITGUEST');
384 }
385
386 // register error message
387 $this->setError($err);
388 return false;
389 }
390 }
391
392 VAPLoader::import('libraries.availability.manager');
393 VAPLoader::import('libraries.availability.timeline.factory');
394
395 // create availability search instance
396 $search = VAPAvailabilityManager::getInstance($options['id_ser'], $options['id_emp'], $options);
397
398 // get details of the selected service
399 $service = JModelVAP::getInstance('serempassoc')->getOverrides($search->get('id_service'), $search->get('id_employee'));
400
401 // define default options
402 $options['people'] = isset($options['people']) ? $options['people'] : 1;
403
404 if ($service)
405 {
406 // number of people cannot be lower than the minimum amount of the service
407 $options['people'] = max(array($service->min_per_res, (int) $options['people']));
408 // number of people cannot be higher than the maximum amount of the service
409 $options['people'] = min(array($service->max_per_res, (int) $options['people']));
410 }
411
412 $options['id_res'] = isset($options['id_res']) ? $options['id_res'] : 0;
413
414 try
415 {
416 // create timeline parser instance
417 $parser = VAPAvailabilityTimelineFactory::getParser($search);
418 }
419 catch (Exception $e)
420 {
421 // register exception as error
422 $this->setError($e);
423
424 return false;
425 }
426
427 // elaborate timeline
428 $timeline = $parser->getTimeline($options['date'], $options['people'], $options['id_res']);
429
430 if (!$timeline)
431 {
432 // propagate error message
433 $this->setError($parser->getError());
434
435 return false;
436 }
437
438 /**
439 * Do not block the times with the appointments within the cart when the
440 * system does not allow multiple bookings. This way, in case the users
441 * go back through the browser history, they are still allowed to book
442 * the previously selected time.
443 *
444 * @since 1.7.4
445 */
446 if (empty($options['admin']) && $config->getBool('enablecart'))
447 {
448 // get cart handler
449 $cart = JModelVAP::getInstance('cart')->getCart();
450
451 // extract duration
452 $duration = $service ? $service->duration : 5;
453
454 foreach ($timeline as $level)
455 {
456 foreach ($level as $time)
457 {
458 // get check-in date time in UTC timezone, since the times in the cart are
459 // always stored by using the UTC offset
460 $checkin = $time->checkin('Y-m-d H:i:s', 'UTC');
461
462 // check whether this time slot intersects an appointment within the cart
463 $index = $cart->indexOf($search->get('id_service'), $search->get('id_employee'), $checkin, $duration);
464
465 if ($index != -1)
466 {
467 // there's a conflict with a booked appointment, mark the time block as occupied
468 $time->setStatus(false);
469 }
470 }
471 }
472 }
473
474 try
475 {
476 // create timeline renderer instance
477 $renderer = VAPAvailabilityTimelineFactory::getRenderer($timeline);
478 }
479 catch (Exception $e)
480 {
481 // register exception as error
482 $this->setError($e);
483
484 return false;
485 }
486
487 return $renderer;
488 }
489
490 /**
491 * Sends an e-mail to the specified employee.
492 *
493 * @param integer $id_employee The employee to notify.
494 * @param string $name The customer name (sender name).
495 * @param string $email The customer e-mail (sender mail).
496 * @param string $content The e-mail content (plain text).
497 *
498 * @return boolean True on success, false otherwise.
499 */
500 public function askQuestion($id_employee, $name, $email, $content)
501 {
502 // validate user data
503 if (!$name || !$email)
504 {
505 // register missing fields error
506 $this->setError(JText::translate('VAPCONFAPPREQUIREDERROR'));
507 return false;
508 }
509
510 // make sure we have a valid e-mail
511 if (!VikAppointments::validateUserEmail($email))
512 {
513 // invalid e-mail
514 $this->setError(JText::translate('VAPCONFAPPREQUIREDMAILERROR'));
515 return false;
516 }
517
518 // strip HTML tags from content
519 $content = strip_tags($content);
520
521 if (!$content)
522 {
523 // missing content
524 $this->setError(JText::translate('VAPQUICKCONTACTNOCONTENT'));
525 return false;
526 }
527
528 // use model to access the employee details
529 $employee = JModelVAP::getInstance('employee')->getItem((int) $id_employee);
530
531 if (!$employee)
532 {
533 // employee not found
534 $this->setError(JText::translate('VAPEMPNOTFOUNDERROR'));
535 return false;
536 }
537
538 if (!$employee->quick_contact)
539 {
540 // the employee doesn't allow quick contact messages
541 $this->setError(JText::translate('VAPEMPNOTREACHABLE'));
542 return false;
543 }
544
545 // fetch e-mail subject
546 $subject = JText::translate('VAPEMPQUICKCONTACTSUBJECT');
547 $is_html = false;
548
549 // define sender details
550 $sender = array('name' => $name, 'email' => $email);
551
552 // set list of recipients
553 $recipients = array($employee->email);
554
555 // prepare hook arguments
556 $args = array($id_employee, &$subject, &$content, &$is_html, &$sender, &$recipients, $this);
557
558 try
559 {
560 /**
561 * Trigger event to allow the plugins to manipulate quick contact messages
562 * for the given employee. It is possible to throw an exception to avoid
563 * sending the message. The exception text will be used as error message.
564 *
565 * @param string $id_emp The employee ID.
566 * @param string &$subject The e-mail subject.
567 * @param string &$content The e-mail content (the customer message).
568 * @param boolean &$is_html True if the e-mail should support HTML tags.
569 * @param string &$sender The e-mail sender details (@since 1.7).
570 * @param array &$recipients A list of recipient e-mails (@since 1.7).
571 * @param JModel $model The current model (@since 1.7).
572 *
573 * @return boolean False to prevent e-mail sending (@since 1.7).
574 *
575 * @throws Exception
576 *
577 * @since 1.6
578 */
579 if (VAPFactory::getEventDispatcher()->false('onBeforeQuickContactSend', $args))
580 {
581 // someone prevented the e-mail sending
582 return false;
583 }
584 }
585 catch (Exception $e)
586 {
587 // catch the error and register the message set
588 $this->setError($e->getMessage());
589 return false;
590 }
591
592 // By default e-mails are always sent by using the customer e-mail/name
593 // as sender details. In case a server rejects certain e-mails, it is
594 // possible to use the hook previously described to change the sender
595 // details with other ones (such as the global sender e-mail).
596
597 $sent = false;
598
599 // iterate all recipients
600 foreach ($recipients as $recipient)
601 {
602 // try to send the e-mail
603 $sent = VAPApplication::getInstance()->sendMail(
604 $sender['email'], // sender e-mail
605 $sender['name'], // sender name
606 $recipient, // recipient e-mail
607 $email, // reply-to e-mail
608 $subject, // e-mail subject
609 $content, // e-mail content
610 null, // attachments
611 $is_html // is HTML flag
612 ) || $sent;
613 }
614
615 if (!$sent)
616 {
617 /**
618 * Register generic error to inform the user that the website
619 * is currently unable to send messages.
620 *
621 * @since 1.7
622 */
623 $this->setError(JText::translate('VAPMAILERR'));
624 return false;
625 }
626
627 return true;
628 }
629
630 ////////////////////////////////////////
631 //////////// HELPER METHODS ////////////
632 ////////////////////////////////////////
633
634 /**
635 * Applies additional queries to fill the employee object
636 * with other data, such as the supported locations.
637 *
638 * @param object $employee The employee details object.
639 * @param array $options An array of options.
640 *
641 * @return object The resulting employee object.
642 */
643 protected function buildEmployeeData($employee, $options)
644 {
645 $dbo = JFactory::getDbo();
646
647 if (!$employee->timezone)
648 {
649 // use default system timezone
650 $employee->timezone = JFactory::getApplication()->get('offset', 'UTC');
651 }
652
653 if ($employee->id_group)
654 {
655 // register group data in a different object
656 $group = new stdClass;
657 $group->id = $employee->id_group;
658 $group->name = $employee->group_name;
659 $group->description = $employee->group_description;
660
661 $employee->group = $group;
662 }
663 else
664 {
665 // no assigned group
666 $employee->group = null;
667 }
668
669 // round rating to the closest .0 or .5
670 $employee->rating = VikAppointments::roundHalfClosest($employee->ratingAVG);
671
672 // load employee reviews
673 $employee->reviews = VikAppointments::loadReviews('employee', $employee->id);
674
675 // get rid of duplicates
676 unset($employee->id_group);
677 unset($employee->group_name);
678 unset($employee->group_description);
679
680 // load all services supported by the employee
681 $employee->services = array();
682
683 $q = $dbo->getQuery(true)
684 ->select($dbo->qn('g.name', 'group_name'))
685 ->select($dbo->qn('s.id'))
686 ->from($dbo->qn('#__vikappointments_ser_emp_assoc', 'a'))
687 ->leftjoin($dbo->qn('#__vikappointments_service', 's') . ' ON ' . $dbo->qn('s.id') . ' = ' . $dbo->qn('a.id_service'))
688 ->leftjoin($dbo->qn('#__vikappointments_group', 'g') . ' ON ' . $dbo->qn('g.id') . ' = ' . $dbo->qn('s.id_group'))
689 ->where($dbo->qn('a.id_employee') . ' = ' . (int) $employee->id)
690 ->where($dbo->qn('s.published') . ' = 1')
691 ->order($dbo->qn('g.ordering') . ' ASC')
692 ->order($dbo->qn('s.ordering') . ' ASC');
693
694 /**
695 * Retrieve only the services that belong to the view
696 * access level of the current user.
697 *
698 * @since 1.6
699 */
700 $levels = JFactory::getUser()->getAuthorisedViewLevels();
701
702 if ($levels)
703 {
704 $q->where($dbo->qn('s.level') . ' IN (' . implode(', ', $levels) . ')');
705 }
706
707 $dbo->setQuery($q);
708
709 if ($services = $dbo->loadObjectList())
710 {
711 // get service-employee association model
712 $assocModel = JModelVAP::getInstance('serempassoc');
713
714 // get current date time
715 $now = JFactory::getDate();
716
717 foreach ($services as $s)
718 {
719 // get service details
720 $service = $assocModel->getOverrides($s->id, $employee->id);
721
722 if (!$service)
723 {
724 // It is totally improbable to enter here, because the services
725 // list have been already loaded from the previous query...
726 continue;
727 }
728
729 // validate service end publishing
730 if (VAPDateHelper::isNull($service->end_publishing)
731 || $service->end_publishing > $now)
732 {
733 if ($service->id_group)
734 {
735 // register group name too
736 $service->groupName = $s->group_name;
737 }
738
739 // service published, register it
740 $employee->services[] = $service;
741 }
742 }
743 }
744
745 if (empty($options['id_service']) || $options['id_service'] <= 0)
746 {
747 $options['id_service'] = null;
748
749 if (count($employee->services))
750 {
751 // use the first available service
752 $options['id_service'] = $employee->services[0]->id;
753 }
754 }
755
756 // fetch employee locations (filter by service if needed)
757 $employee->locations = $this->getLocations($employee->id, $options['id_service']);
758
759 // load options assigned to the selected service
760 if ($options['id_service'])
761 {
762 // get service search view model instance
763 $serviceSearchModel = JModelVAP::getInstance('servicesearch');
764 // load options
765 $employee->options = $serviceSearchModel->getOptions($options['id_service']);
766 }
767 else
768 {
769 $employee->options = array();
770 }
771
772 return $employee;
773 }
774
775 /**
776 * Translates the employee details.
777 *
778 * @param object &$employee The row to translate.
779 *
780 * @return void
781 */
782 protected function translate(&$employee)
783 {
784 /**
785 * Ignore translation in case the multilingual feature is disabled.
786 *
787 * @since 1.7.4
788 */
789 if (VAPFactory::getConfig()->getBool('ismultilang') == false)
790 {
791 return;
792 }
793
794 $langtag = JFactory::getLanguage()->getTag();
795
796 // get translator
797 $translator = VAPFactory::getTranslator();
798
799 // translate employee for the given language
800 $emp_tx = $translator->translate('employee', $employee->id, $langtag);
801
802 if ($emp_tx)
803 {
804 $employee->nickname = $emp_tx->nickname;
805 $employee->note = $emp_tx->note;
806 }
807
808 if ($employee->group)
809 {
810 // translate group for the given language
811 $grp_tx = $translator->translate('empgroup', $employee->group->id, $langtag);
812
813 if ($grp_tx)
814 {
815 $employee->group->name = $grp_tx->name;
816 $employee->group->description = $grp_tx->description;
817 }
818 }
819
820 $service_ids = array();
821 $group_ids = array();
822
823 // map all existing services and groups
824 foreach ($employee->services as $service)
825 {
826 $service_ids[] = $service->id;
827
828 if ($service->id_group > 0)
829 {
830 $group_ids[] = $service->id_group;
831 }
832 }
833
834 // preload services translations
835 $serLang = $translator->load('service', array_unique($service_ids), $langtag);
836 // preload groups translations
837 $grpLang = $translator->load('group', array_unique($group_ids), $langtag);
838
839 // apply translations found
840 foreach ($employee->services as $service)
841 {
842 // get service translation
843 $ser_tx = $serLang->getTranslation($service->id, $langtag);
844
845 if ($ser_tx)
846 {
847 // use service name and description translations
848 $service->name = $ser_tx->name;
849 $service->description = $ser_tx->description;
850
851 /**
852 * The description might have overwritten the override
853 * for this specific employee/service combination. For
854 * this reason we need to re-apply it, if existing.
855 *
856 * @since 1.7
857 */
858 if (!empty($service->overrideDescription))
859 {
860 $service->description .= "\n" . $service->overrideDescription;
861 }
862 }
863
864 if ($service->id_group > 0)
865 {
866 // get group translation
867 $grp_tx = $grpLang->getTranslation($service->id_group, $langtag);
868
869 if ($grp_tx)
870 {
871 // use group name translation
872 $service->groupName = $grp_tx->name;
873 }
874 }
875 }
876 }
877 }
878