<?php /** * @package VikBooking * @subpackage core * @author Alessio Gaggii - E4J s.r.l. * @copyright Copyright (C) 2024 E4J s.r.l. All Rights Reserved. * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL * @link https://vikwp.com */ // No direct access defined('ABSPATH') or die('No script kiddies please!'); /** * Notification elements registry to store a record for the Notification Center. * * @since 1.16.8 (J) - 1.6.8 (WP) */ final class VBONotificationElements extends JObject { /** * Valid notification group enumerations. * * @var array */ private $groupEnums = [ // notifications from Website 'website', // notifications from OTAs 'otas', // notifications from Channel Manager 'cm', // notifications from Guests 'guests', // notifications from Operators 'operators', // notifications from (PMS) Reports 'reports', // notifications from AI 'ai', ]; /** * Default notification type. * * @var string */ private $defaultType = 'info'; /** * Determines and returns the group to which the notification belongs. * * @return string */ public function getGroup() { // check if the group was already determined $group = (string) $this->get('_group', ''); if (!$group) { // access the notification sender property $group = strtolower((string) $this->get('sender', $this->groupEnums[0])); // validate the group property $group = in_array($group, $this->groupEnums) ? $group : $this->groupEnums[0]; // cache determined group $this->set('_group', $group); } return $group; } /** * Returns the notification type. * * @return string */ public function getType() { // access the notification type $type = strtolower((string) $this->get('type', $this->defaultType)); // ensure the maximum length of 32 chars is respected return $this->shortenString($type, 32, $this->defaultType); } /** * Returns the notification title. * * @return string */ public function getTitle() { // access the notification title $title = (string) $this->get('title', ''); // try to guess the title if (!$title) { // check for virtual credit card balance if (strpos($this->getType(), 'vcc_balance') === 0) { $title = JText::translate('VBO_VCC_BALANCE'); if ($title == 'VBO_VCC_BALANCE') { $title = ucwords(str_replace('_', ' ', $this->getType())); } } } // ensure the maximum length of 64 chars is respected return $this->shortenString($title, 64); } /** * Returns the notification summary. * * @return string */ public function getSummary() { // access the notification summary $summary = (string) $this->get('summary', ''); // normalize the summary, if needed if ($summary && $this->getChannel() && !strcasecmp($this->getType(), 'lvf')) { // listing verification framework (LVF) $summary = JText::sprintf('VBO_VERIFY_LISTING_INFO', $summary); } // ensure the maximum length of 256 chars is respected return $this->shortenString($summary, 256); } /** * Returns the notification avatar URI. * * @return string * * @since 1.18.0 (J) - 1.8.0 (WP) */ public function getAvatar() { // access the notification avatar $avatar = (string) $this->get('avatar', ''); // turn full internal URIs into relative URIs $avatar = str_replace(JUri::root(), '', $avatar); // ensure the maximum length of 256 chars is respected return $this->shortenString($avatar, 256); } /** * Builds and returns the notification call-to-action data. * * @return null|string */ public function getCallToActionData() { $cta_data = []; // check if a widget identifier was provided $widget = (string) $this->get('widget', ''); if ($widget) { $cta_data['widget'] = $widget; } // check if some widget options were provided $widget_options = (array) $this->get('widget_options', []); if ($widget_options) { $cta_data['widget_options'] = $widget_options; } // check if a notification URL was provided $cta_url = (string) $this->get('cta_url', $this->get('url')); if ($cta_url) { $cta_data['url'] = $cta_url; } // check if a custom label was provided if ($cta_data && is_string($this->get('label'))) { $cta_data['label'] = $this->get('label'); } if (!$cta_data) { // attempt to determine the CTA payload to set if (strpos($this->getType(), 'vcc_balance') === 0 && ($this->getReservationID() || $this->getOTAReservationID())) { // set call-to-action for Virtual Terminal admin-widget for VCC balance $cta_data = [ 'label' => JText::translate('VBO_CC_DOCHARGE'), 'widget' => 'virtual_terminal', 'widget_options' => [ 'bid' => $this->getReservationID() ?: $this->getOTAReservationID(), ], ]; } elseif (!strcasecmp($this->getType(), 'guest_message') && ($this->getReservationID() || $this->getOTAReservationID())) { // set call-to-action for Guest Messages admin-widget to reply to the guest message $cta_data = [ 'label' => JText::translate('VBO_REPLY'), 'widget' => 'guest_messages', 'widget_options' => [ 'bid' => $this->getReservationID() ?: $this->getOTAReservationID(), ], ]; } elseif (!strcasecmp($this->getType(), 'ob') && $this->getReservationID()) { // set call-to-action for Bookings Calendar admin-widget in case of overbooking $cta_data = [ 'widget' => 'bookings_calendar', 'widget_options' => [ 'bid' => $this->getReservationID(), 'overbooking' => 1, ], ]; } } return $cta_data ? json_encode($cta_data) : null; } /** * Returns the VikBooking reservation ID. * * @return null|int */ public function getReservationID() { $res_id = $this->get('idorder', null); return $res_id ? (int) $res_id : null; } /** * Returns the OTA reservation ID. * * @return null|string */ public function getOTAReservationID() { $ota_res_id = $this->get('idorderota', null); return $ota_res_id ? (string) $ota_res_id : null; } /** * Returns the channel name. * * @return null|string */ public function getChannel() { $channel = $this->get('channel', null); return $channel ? (string) $channel : null; } /** * Returns the notification date and time. * * @return string */ public function getDate() { try { $date = JFactory::getDate($this->get('date') ?: 'now'); } catch(Exception $e) { $date = JFactory::getDate(); } return $date->toSql(); } /** * Returns the notification signature. * * @return string */ public function getSignature() { $signature = (string) $this->get('_signature', ''); if (!$signature) { $signature = $this->buildSignature(); } return $signature; } /** * Sets the notification signature. * * @return self */ public function setSignature(string $signature = '') { $this->set('_signature', $signature); return $this; } /** * Builds, sets and returns the notification signature. * * @return string */ public function buildSignature() { // build notification signature elements $elements = [ 'id' => $this->get('id', $this->get('notification_id', 0)), 'idorder' => $this->get('idorder', 0), 'sender' => $this->get('sender', ''), 'type' => $this->get('type', ''), 'title' => $this->get('title', ''), 'summary' => $this->get('summary', ''), ]; // build notification signature string $signature = md5(serialize($elements)); // set signature string $this->setSignature($signature); return $signature; } /** * Returns the notification error code. * * @return int */ public function getErrorCode() { return (int) $this->get('_errorCode', 500); } /** * Sets the notification error code. * * @return self */ public function setErrorCode($code = 500) { $this->set('_errorCode', $code); return $this; } /** * Ensures a string reflects the given length, or it will be eventually shortened or replaced. * * @param string $value the string to check. * @param int $length the length to reflect. * @param string $fallback optional string to replace as fallback. * * @return string */ private function shortenString(string $value, int $length, string $fallback = '') { if (strlen($value) <= $length || $length <= 0) { // length is safe return $value; } if ($fallback) { // replace string with provided fallback return $fallback; } // shorten the string to the desired length if (!function_exists('mb_strlen')) { // use a regular sub-string without multi-byte support return rtrim(substr($value, 0, $length - 3), '.,?!;:#\'"([{ ') . '...'; } // calculate string length $size = strlen($value); $mb_size = mb_strlen($value); $ch_diff = $size - $mb_size; if ($ch_diff <= 0) { // no multi-byte chars found return rtrim(substr($value, 0, $length - 3), '.,?!;:#\'"([{ ') . '...'; } // safely construct the string with one multibyte char per time $mb_value = ''; $mb_char_start = 0; while (strlen($mb_value) < $length - 3) { // get a one-char multi-byte portion $mb_portion = mb_substr($value, $mb_char_start, 1, 'UTF-8'); if (strlen($mb_value . $mb_portion) > $length) { // abort to not exceed the length return $mb_value; } // add portion to string value $mb_value .= $mb_portion; // increase chart start counter $mb_char_start++; } return rtrim($mb_value, '.,?!;:#\'"([{ ') . '...'; } }