| @@ -2,9 +2,9 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * @package VikBooking |
| 4 | 4 | * @subpackage core |
| 5 | 5 | * @author E4J s.r.l. |
| 6 | - * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved. | |
| 6 | + * @copyright Copyright (C) 2026 E4J s.r.l. All Rights Reserved. | |
| 7 | 7 | * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL |
| 8 | 8 | * @link https://vikwp.com |
| 9 | 9 | */ |
| 10 | 10 | |
| @@ -35,17 +35,19 @@ | ||
| 35 | 35 | /** |
| 36 | 36 | * The path where the attachments are internally stored. |
| 37 | 37 | * |
| 38 | 38 | * @var string |
| 39 | + * @since 1.8.8 Changed visibility to public. | |
| 39 | 40 | */ |
| 40 | - protected $attachmentsPath; | |
| 41 | + public $attachmentsPath; | |
| 41 | 42 | |
| 42 | 43 | /** |
| 43 | 44 | * A string holding all the supported file extensions, separated by a comma. |
| 44 | 45 | * |
| 45 | 46 | * @var string |
| 47 | + * @since 1.8.8 Changed visibility to public. | |
| 46 | 48 | */ |
| 47 | - protected $supportedFiles; | |
| 49 | + public $supportedFiles; | |
| 48 | 50 | |
| 49 | 51 | /** |
| 50 | 52 | * Class constructor. |
| 51 | 53 | * |
| @@ -60,13 +62,13 @@ | ||
| 60 | 62 | |
| 61 | 63 | // create default attachments extension filters |
| 62 | 64 | $this->supportedFiles = implode(',', [ |
| 63 | 65 | // images |
| 64 | - 'png,apng,bmp,gif,ico,jpg,jpeg,svg,heic,webp', | |
| 66 | + 'png,apng,bmp,gif,ico,jpg,jpeg,heic,webp', | |
| 65 | 67 | // videos |
| 66 | 68 | 'mp4,mov,ogm,webm,3gp,asf,avi,divx,flv,mkv,mpg,mpeg,wmv,xvid', |
| 67 | 69 | // audios |
| 68 | - 'aac,m4a,mp3,opus,wav,wave,ac3,aiff,flac,mid,midi,wma', | |
| 70 | + 'aac,m4a,mp3,ogg,opus,wav,wave,ac3,aiff,flac,mid,midi,wma', | |
| 69 | 71 | // archives |
| 70 | 72 | 'zip,tar,rar,gz,bzip2', |
| 71 | 73 | // documents |
| 72 | 74 | 'pdf,doc,docx,rtf,odt,pages,txt,md,markdown', |
| @@ -92,11 +94,33 @@ | ||
| 92 | 94 | // auto-bind the sender only if not provided |
| 93 | 95 | if (JFactory::getApplication()->isClient('administrator')) { |
| 94 | 96 | // authenticate as administrator |
| 95 | 97 | $user = new VBOChatUserAdmin; |
| 98 | + } else if (class_exists('VCMAppFramework') && ($appUser = VCMAppFramework::getUser())) { | |
| 99 | + /** | |
| 100 | + * The user authenticated via APP. | |
| 101 | + * Create a chat account accordingly. | |
| 102 | + * | |
| 103 | + * @since 1.18.16 (J) - 1.8.16 (WP) | |
| 104 | + */ | |
| 105 | + if ($appUser['admin']) { | |
| 106 | + // authenticate as administrator | |
| 107 | + $user = new VBOChatUserAdmin(new JUser($appUser['id'])); | |
| 108 | + } else { | |
| 109 | + // authenticate as operator | |
| 110 | + $user = new VBOChatUserOperator($appUser['id']); | |
| 111 | + } | |
| 96 | 112 | } else { |
| 97 | - // authenticate as operator | |
| 98 | - $user = new VBOChatUserOperator; | |
| 113 | + // fetch details of the logged in operator | |
| 114 | + $operator = VikBooking::getOperatorInstance()->getOperatorAccount(); | |
| 115 | + | |
| 116 | + if ($operator) { | |
| 117 | + // authenticate as operator | |
| 118 | + $user = new VBOChatUserOperator($operator); | |
| 119 | + } else { | |
| 120 | + // authenticate as guest user | |
| 121 | + $user = new VBOChatUserGuest; | |
| 122 | + } | |
| 99 | 123 | } |
| 100 | 124 | } |
| 101 | 125 | |
| 102 | 126 | $this->user = $user; |
| @@ -162,9 +186,9 @@ | ||
| 162 | 186 | // force the sender name and ID according to the details of the logged in user |
| 163 | 187 | $message->setSender($user->getName(), $user->getID()); |
| 164 | 188 | |
| 165 | 189 | // attempt to save the message |
| 166 | - $this->storage->saveMessage($message); | |
| 190 | + $this->saveMessage($message); | |
| 167 | 191 | |
| 168 | 192 | // iterate all the users that should receive a notification |
| 169 | 193 | foreach ($message->getContext()->getRecipients() as $recipient) { |
| 170 | 194 | if ($message->getSenderID() == $recipient->getID()) { |
| @@ -179,8 +203,42 @@ | ||
| 179 | 203 | } |
| 180 | 204 | } |
| 181 | 205 | |
| 182 | 206 | /** |
| 207 | + * Saves the provided message. | |
| 208 | + * | |
| 209 | + * @param VBOChatMessage $message The message to save. | |
| 210 | + * | |
| 211 | + * @return void | |
| 212 | + * | |
| 213 | + * @throws Exception | |
| 214 | + * | |
| 215 | + * @since 1.8.8 | |
| 216 | + */ | |
| 217 | + public function saveMessage(VBOChatMessage $message) | |
| 218 | + { | |
| 219 | + // save the message | |
| 220 | + $this->storage->saveMessage($message); | |
| 221 | + } | |
| 222 | + | |
| 223 | + /** | |
| 224 | + * Removes the provided message. | |
| 225 | + * | |
| 226 | + * @param VBOChatMessage $message The message to delete. | |
| 227 | + * | |
| 228 | + * @return void | |
| 229 | + * | |
| 230 | + * @throws Exception | |
| 231 | + * | |
| 232 | + * @since 1.8.8 | |
| 233 | + */ | |
| 234 | + public function deleteMessage(VBOChatMessage $message) | |
| 235 | + { | |
| 236 | + // delete the message | |
| 237 | + $this->storage->deleteMessage($message); | |
| 238 | + } | |
| 239 | + | |
| 240 | + /** | |
| 183 | 241 | * Moves the uploaded temporary file onto the server and creates a new attachment. |
| 184 | 242 | * |
| 185 | 243 | * @param array $file The temporary file under $_FILES. |
| 186 | 244 | * |
| @@ -222,9 +280,29 @@ | ||
| 222 | 280 | if (!$attachment->exists()) { |
| 223 | 281 | return false; |
| 224 | 282 | } |
| 225 | 283 | |
| 226 | - return JFile::delete($attachment->getPath()); | |
| 284 | + $filePath = $attachment->getPath(); | |
| 285 | + | |
| 286 | + /** | |
| 287 | + * Resolve traversal segments before comparing the received path | |
| 288 | + * against the allowed folder. | |
| 289 | + * | |
| 290 | + * @since 1.18.11 (J) - 1.8.11 (WP) | |
| 291 | + */ | |
| 292 | + $filePath = realpath($filePath); | |
| 293 | + | |
| 294 | + /** | |
| 295 | + * Make sure the file is located under the registered attachments path | |
| 296 | + * to prevent arbitrary file deletion. | |
| 297 | + * | |
| 298 | + * @since 1.18.10 (J) - 1.8.10 (WP) | |
| 299 | + */ | |
| 300 | + if (!$filePath || strpos($filePath, $this->attachmentsPath) !== 0) { | |
| 301 | + return false; | |
| 302 | + } | |
| 303 | + | |
| 304 | + return JFile::delete($filePath); | |
| 227 | 305 | } |
| 228 | 306 | |
| 229 | 307 | /** |
| 230 | 308 | * Reads all the messages under the specified context for the currently logged in user. |
| @@ -234,9 +312,10 @@ | ||
| 234 | 312 | * lower than this value will be read. |
| 235 | 313 | * |
| 236 | 314 | * @return int[] A list of read message IDs. |
| 237 | 315 | */ |
| 238 | - public function readMessages(VBOChatContext $context, ?string $date = null) { | |
| 316 | + public function readMessages(VBOChatContext $context, ?string $date = null) | |
| 317 | + { | |
| 239 | 318 | $search = (new VBOChatSearch) |
| 240 | 319 | // take the latest 50 unread messages |
| 241 | 320 | ->start(0)->limit(50)->unread() |
| 242 | 321 | // under the specified context |
| @@ -264,8 +343,25 @@ | ||
| 264 | 343 | return $read; |
| 265 | 344 | } |
| 266 | 345 | |
| 267 | 346 | /** |
| 347 | + * Read the provided message. | |
| 348 | + * | |
| 349 | + * @param VBOChatMessage $message The message to read. | |
| 350 | + * | |
| 351 | + * @return void | |
| 352 | + * | |
| 353 | + * @throws Exception | |
| 354 | + * | |
| 355 | + * @since 1.8.8 | |
| 356 | + */ | |
| 357 | + public function readMessage(VBOChatMessage $message) | |
| 358 | + { | |
| 359 | + // read the message | |
| 360 | + $this->storage->readMessage($message->getID(), $this->getUser()->getID()); | |
| 361 | + } | |
| 362 | + | |
| 363 | + /** | |
| 268 | 364 | * Creates a new message object. |
| 269 | 365 | * |
| 270 | 366 | * @param object|array $message The raw message record. |
| 271 | 367 | * |
| @@ -312,9 +408,9 @@ | ||
| 312 | 408 | if (!class_exists($classname)) { |
| 313 | 409 | throw new RuntimeException('The class [' . $classname . '] does not exist.', 404); |
| 314 | 410 | } |
| 315 | 411 | |
| 316 | - // instantiate class by inject the provided ID | |
| 412 | + // instantiate class by injecting the provided ID | |
| 317 | 413 | return new $classname($id); |
| 318 | 414 | } |
| 319 | 415 | |
| 320 | 416 | /** |
| @@ -348,10 +444,14 @@ | ||
| 348 | 444 | $document = JFactory::getDocument(); |
| 349 | 445 | $document->addScript(VBO_SITE_URI . 'resources/chat.js'); |
| 350 | 446 | $document->addStyleSheet(VBO_SITE_URI . 'resources/chat.css'); |
| 351 | 447 | |
| 448 | + /** @var VBOChatUser */ | |
| 449 | + $user = $this->getUser(); | |
| 450 | + | |
| 352 | 451 | // load assets for each supported context |
| 353 | - (new VBOChatContextTask(0))->useAssets(); | |
| 452 | + (new VBOChatContextTask(0))->useAssets($user); | |
| 453 | + (new VBOChatContextSession(0))->useAssets($user); | |
| 354 | 454 | |
| 355 | 455 | return $this; |
| 356 | 456 | } |
| 357 | 457 | |
| @@ -371,8 +471,11 @@ | ||
| 371 | 471 | if ($options['assets'] ?? true) { |
| 372 | 472 | $this->useAssets(); |
| 373 | 473 | } |
| 374 | 474 | |
| 475 | + /** @var VBOChatUser */ | |
| 476 | + $user = $this->getUser(); | |
| 477 | + | |
| 375 | 478 | // load the latest 20 messages of the specified context |
| 376 | 479 | $messages = $this->getMessages( |
| 377 | 480 | (new VBOChatSearch)->limit($options['limit'] ?? 20)->withContext($context) |
| 378 | 481 | ); |
| @@ -400,14 +503,15 @@ | ||
| 400 | 503 | return $layout->render([ |
| 401 | 504 | 'uri' => $ajaxUri, |
| 402 | 505 | 'messages' => $messages, |
| 403 | 506 | 'users' => $users, |
| 404 | - 'user' => $this->getUser(), | |
| 507 | + 'user' => $user, | |
| 405 | 508 | 'options' => $options, |
| 406 | 509 | 'context' => [ |
| 407 | 510 | 'id' => $context->getID(), |
| 408 | 511 | 'alias' => $context->getAlias(), |
| 409 | - 'actions' => $context->getActions(), | |
| 512 | + 'actions' => $context->getActions($user), | |
| 513 | + 'metadata' => $context->getMetadata(true), | |
| 410 | 514 | ], |
| 411 | 515 | ]); |
| 412 | 516 | } |
| 413 | 517 | } |