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 |