| @@ -161,9 +161,9 @@ | ||
| 161 | 161 | * @param int $lim query limit count. |
| 162 | 162 | * @param array $filters optional list of column filters. |
| 163 | 163 | * @param int $min_id optional minimum notification ID. |
| 164 | 164 | */ |
| 165 | - public function loadNotifications(int $start = 0, int $lim = null, array $filters = [], int $min_id = 0) | |
| 165 | + public function loadNotifications(int $start = 0, int $lim = 0, array $filters = [], int $min_id = 0) | |
| 166 | 166 | { |
| 167 | 167 | $dbo = JFactory::getDbo(); |
| 168 | 168 | |
| 169 | 169 | $q = $dbo->getQuery(true) |
| @@ -391,15 +391,22 @@ | ||
| 391 | 391 | 'new_notifications' => 0, |
| 392 | 392 | ]; |
| 393 | 393 | |
| 394 | 394 | foreach ($notifications as $notification) { |
| 395 | - // wrap the notification array/object into a registry | |
| 396 | - $notif_registry = new VBONotificationElements($notification); | |
| 395 | + // wrap the notification array/object into a proper type-registry | |
| 396 | + $notif_registry = VBONotificationElements::getInstance($notification); | |
| 397 | 397 | |
| 398 | 398 | if ($this->storeNotification($notif_registry)) { |
| 399 | 399 | // record was stored successfully |
| 400 | 400 | $result['new_notifications']++; |
| 401 | 401 | |
| 402 | + /** | |
| 403 | + * Trigger a postflight action for this type of notification. | |
| 404 | + * | |
| 405 | + * @since 1.18.3 (J) - 1.8.3 (WP) | |
| 406 | + */ | |
| 407 | + $notif_registry->postflight(); | |
| 408 | + | |
| 402 | 409 | // parse the next one |
| 403 | 410 | continue; |
| 404 | 411 | } |
| 405 | 412 | |
| @@ -420,8 +427,10 @@ | ||
| 420 | 427 | * @return bool |
| 421 | 428 | */ |
| 422 | 429 | private function storeNotification(VBONotificationElements $notification) |
| 423 | 430 | { |
| 431 | + $dbo = JFactory::getDbo(); | |
| 432 | + | |
| 424 | 433 | if (!$notification->get('sender') || !$notification->get('type')) { |
| 425 | 434 | $notification->setError('Notification sender and type are mandatory'); |
| 426 | 435 | return false; |
| 427 | 436 | } |
| @@ -435,24 +444,29 @@ | ||
| 435 | 444 | // ensure language definitions are loaded for non-website notifications |
| 436 | 445 | $this->loadLanguageDefs(); |
| 437 | 446 | } |
| 438 | 447 | |
| 448 | + // build notification record to store or update | |
| 449 | + $record = new stdClass; | |
| 450 | + | |
| 439 | 451 | // ensure the same notification does not exist already |
| 440 | - if ($this->signatureExists($notification->getSignature())) { | |
| 441 | - // abort without raising any errors to skip a duplicate notification | |
| 452 | + if ($duplicate_id = $this->signatureExists($notification->getSignature())) { | |
| 453 | + // update the date for the existing notification ID | |
| 454 | + $record->id = $duplicate_id; | |
| 455 | + $record->createdon = JFactory::getDate('now')->toSql(); | |
| 456 | + $dbo->updateObject('#__vikbooking_notifications', $record, 'id'); | |
| 457 | + | |
| 458 | + // abort without raising any errors to skip a duplicate notification from being created | |
| 442 | 459 | return false; |
| 443 | 460 | } |
| 444 | 461 | |
| 445 | - $dbo = JFactory::getDbo(); | |
| 446 | - | |
| 447 | - // build notification record to store | |
| 448 | - $record = new stdClass; | |
| 449 | - | |
| 462 | + // set record properties for creation | |
| 450 | 463 | $record->signature = $notification->getSignature(); |
| 451 | 464 | $record->group = $notification->getGroup(); |
| 452 | 465 | $record->type = $notification->getType(); |
| 453 | 466 | $record->title = $notification->getTitle(); |
| 454 | 467 | $record->summary = $notification->getSummary(); |
| 468 | + $record->avatar = $notification->getAvatar(); | |
| 455 | 469 | $record->cta_data = $notification->getCallToActionData(); |
| 456 | 470 | $record->idorder = $notification->getReservationID(); |
| 457 | 471 | $record->idorderota = $notification->getOTAReservationID(); |
| 458 | 472 | $record->channel = $notification->getChannel(); |
| @@ -490,8 +504,9 @@ | ||
| 490 | 504 | 'sender' => 'guests', |
| 491 | 505 | 'type' => 'guest_message', |
| 492 | 506 | 'title' => JText::sprintf('VBO_MESSAGE_FROM', $message->sender_name ?? 'guest'), |
| 493 | 507 | 'summary' => $message->content ?? '', |
| 508 | + 'avatar' => $message->avatar ?? null, | |
| 494 | 509 | 'idorder' => $thread->idorder ?? null, |
| 495 | 510 | 'idorderota' => $thread->idorderota ?? null, |
| 496 | 511 | 'channel' => $thread->channel ?? null, |
| 497 | 512 | ]; |
| @@ -532,8 +547,9 @@ | ||
| 532 | 547 | $config->set('nc_last_download', $today_dt); |
| 533 | 548 | |
| 534 | 549 | // check if automatic notifications should be generated. |
| 535 | 550 | if (!empty($last_download)) { |
| 551 | + // past month notification | |
| 536 | 552 | $last_info = getdate(strtotime($last_download)); |
| 537 | 553 | $prev_m_info = getdate(strtotime('-1 month')); |
| 538 | 554 | if ($last_info['mon'] == $prev_m_info['mon'] && $last_info['year'] == $prev_m_info['year']) { |
| 539 | 555 | // we are on a new month, generate a finance notification for the past month |
| @@ -538,8 +554,37 @@ | ||
| 538 | 554 | if ($last_info['mon'] == $prev_m_info['mon'] && $last_info['year'] == $prev_m_info['year']) { |
| 539 | 555 | // we are on a new month, generate a finance notification for the past month |
| 540 | 556 | $this->generatePastMonthNotification($last_info); |
| 541 | 557 | } |
| 558 | + | |
| 559 | + /** | |
| 560 | + * Check if there are some pending AI training drafts to review. | |
| 561 | + * | |
| 562 | + * @since 1.18.6 (J) - 1.8.6 (WP) | |
| 563 | + */ | |
| 564 | + try { | |
| 565 | + if (!class_exists('VCMAiModelTraining')) { | |
| 566 | + // do not proceed | |
| 567 | + throw new Exception('E4jConnect Channel Manager unavailable or unsupported.', 500); | |
| 568 | + } | |
| 569 | + // load the draft training sets | |
| 570 | + $response = (new VCMAiModelTraining)->getItems([ | |
| 571 | + // needs review | |
| 572 | + 'status' => 2, | |
| 573 | + ], [ | |
| 574 | + 'ordering' => 'created', | |
| 575 | + 'direction' => 'asc', | |
| 576 | + 'offset' => 0, | |
| 577 | + 'limit' => 1, | |
| 578 | + ]); | |
| 579 | + | |
| 580 | + if ($response->pagination->total ?? 0) { | |
| 581 | + // there are some pending drafts to review, generate a notification | |
| 582 | + $this->generatePendingAiDraftsNotification((int) $response->pagination->total); | |
| 583 | + } | |
| 584 | + } catch (Exception $e) { | |
| 585 | + // do nothing | |
| 586 | + } | |
| 542 | 587 | } |
| 543 | 588 | |
| 544 | 589 | // check if the Channel Manager can download new notifications |
| 545 | 590 | |
| @@ -564,11 +609,11 @@ | ||
| 564 | 609 | |
| 565 | 610 | /** |
| 566 | 611 | * Checks if a notification exists from the given signature. |
| 567 | 612 | * |
| 568 | - * @param string $signature the notification elements signature. | |
| 613 | + * @param string $signature The notification elements signature. | |
| 569 | 614 | * |
| 570 | - * @return bool | |
| 615 | + * @return int|null Either the existing record ID or null. | |
| 571 | 616 | */ |
| 572 | 617 | private function signatureExists(string $signature) |
| 573 | 618 | { |
| 574 | 619 | $dbo = JFactory::getDbo(); |
| @@ -579,13 +624,9 @@ | ||
| 579 | 624 | ->from($dbo->qn('#__vikbooking_notifications')) |
| 580 | 625 | ->where($dbo->qn('signature') . ' = ' . $dbo->q($signature)) |
| 581 | 626 | ); |
| 582 | 627 | |
| 583 | - if ($dbo->loadResult()) { | |
| 584 | - return true; | |
| 585 | - } | |
| 586 | - | |
| 587 | - return false; | |
| 628 | + return $dbo->loadResult(); | |
| 588 | 629 | } |
| 589 | 630 | |
| 590 | 631 | /** |
| 591 | 632 | * Generates a financial notification for the stats of the past month. |
| @@ -622,8 +663,38 @@ | ||
| 622 | 663 | 'widget_options' => [ |
| 623 | 664 | 'fromdate' => date('Y-m-01', $month[0]), |
| 624 | 665 | 'todate' => date('Y-m-t', $month[0]), |
| 625 | 666 | 'type' => 'month', |
| 667 | + ], | |
| 668 | + ]; | |
| 669 | + | |
| 670 | + try { | |
| 671 | + // store the notification(s) | |
| 672 | + $this->store([$notification]); | |
| 673 | + } catch (Throwable $e) { | |
| 674 | + // silently catch the error | |
| 675 | + } | |
| 676 | + } | |
| 677 | + | |
| 678 | + /** | |
| 679 | + * Generates a notification for the pending AI training drafts to review. | |
| 680 | + * | |
| 681 | + * @param int $count The number of pending AI drafts to review. | |
| 682 | + * | |
| 683 | + * @return void | |
| 684 | + */ | |
| 685 | + private function generatePendingAiDraftsNotification(int $count) | |
| 686 | + { | |
| 687 | + // build past month finance notification payload | |
| 688 | + $notification = [ | |
| 689 | + 'sender' => 'ai', | |
| 690 | + 'type' => 'training.drafts', | |
| 691 | + 'title' => JText::translate('VBO_W_AITRAININGDRAFTS_TITLE'), | |
| 692 | + 'summary' => JText::plural('VBO_AITRAININGDRAFTS_SUMMARY_COUNT', $count), | |
| 693 | + 'widget' => 'ai_training_drafts', | |
| 694 | + 'widget_options' => [ | |
| 695 | + 'trigger' => 'nc', | |
| 696 | + 'dt' => date('Y-m-d'), | |
| 626 | 697 | ], |
| 627 | 698 | ]; |
| 628 | 699 | |
| 629 | 700 | try { |