Create New Item
Item Type
File
Folder
Item Name
Search file in folder and subfolders...
Are you sure want to rename?
File Manager
/
wp-content
/
plugins
/
vikbooking
/
admin
/
helpers
/
src
/
chat
:
mediator.php
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * @package VikBooking * @subpackage core * @author E4J s.r.l. * @copyright Copyright (C) 2021 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!'); /** * Chat mediator class. * * @since 1.8 */ class VBOChatMediator { /** * The storage engine used for input/output purposes. * * @var VBOChatStorage */ protected $storage; /** * The currently authenticated user. * * @var VBOChatUser */ private $user; /** * The path where the attachments are internally stored. * * @var string */ protected $attachmentsPath; /** * A string holding all the supported file extensions, separated by a comma. * * @var string */ protected $supportedFiles; /** * Class constructor. * * @param VBOChatStorage $storage */ public function __construct(VBOChatStorage $storage) { $this->storage = $storage; // create default attachments folder $this->attachmentsPath = (defined('VBO_MEDIA_PATH') ? VBO_MEDIA_PATH : '') . DIRECTORY_SEPARATOR . 'attachments'; // create default attachments extension filters $this->supportedFiles = implode(',', [ // images 'png,apng,bmp,gif,ico,jpg,jpeg,svg,heic,webp', // videos 'mp4,mov,ogm,webm,3gp,asf,avi,divx,flv,mkv,mpg,mpeg,wmv,xvid', // audios 'aac,m4a,mp3,opus,wav,wave,ac3,aiff,flac,mid,midi,wma', // archives 'zip,tar,rar,gz,bzip2', // documents 'pdf,doc,docx,rtf,odt,pages,txt,md,markdown', // spreedsheets 'xls,xlsx,csv,ods,numbers', // presentations 'pps,ppsx,odp,keynote', ]); } /** * Authenticates as the provided user. * When no user is passed, the system will attempt to auto-login according * to the client and session data. * * @param VBOChatUser|null $user * * @return self */ public function authenticate(?VBOChatUser $user = null) { if ($user === null) { // auto-bind the sender only if not provided if (JFactory::getApplication()->isClient('administrator')) { // authenticate as administrator $user = new VBOChatUserAdmin; } else { // authenticate as operator $user = new VBOChatUserOperator; } } $this->user = $user; return $this; } /** * Returns the currently logged in user. * In case of missing authentication, the system will attempt to * perform an auto-login. * * @return VBOChatUser */ public function getUser() { if (!$this->user) { // no authenticated user, auto-login now $this->authenticate(); } return $this->user; } /** * Returns the messages matching the specified search query. * * @param VBOChatSearch $search * * @return VBOChatMessage[] */ public function getMessages(VBOChatSearch $search) { $messages = []; if (!$search->hasReader()) { // forces the current user as the reader $search->reader($this->getUser()->getID()); } // pull the messages from the storage $rows = $this->storage->getMessages($search); foreach ($rows as $raw) { // wrap raw record within a message object $messages[] = $this->createMessage($raw); } return $messages; } /** * Sends a new message to all the recipients of the context. * * @param VBOChatMessage $message * * @return void */ public function send(VBOChatMessage $message) { $user = $this->getUser(); // force the sender name and ID according to the details of the logged in user $message->setSender($user->getName(), $user->getID()); // attempt to save the message $this->storage->saveMessage($message); // iterate all the users that should receive a notification foreach ($message->getContext()->getRecipients() as $recipient) { if ($message->getSenderID() == $recipient->getID()) { // do not notify myself continue; } if ($recipient instanceof VBOChatNotifiable) { // schedule message notification $recipient->scheduleNotification($message, $user); } } } /** * Moves the uploaded temporary file onto the server and creates a new attachment. * * @param array $file The temporary file under $_FILES. * * @return VBOChatAttachment * * @throws Exception */ public function uploadAttachment(array $file) { // assert attachments folder first if (!JFolder::exists($this->attachmentsPath) && !JFolder::create($this->attachmentsPath)) { throw new \RuntimeException('Unable to create the attachments folder: ' . $this->attachmentsPath, 403); } // create upload attachment $attachment = new VBOChatAttachmentUpload($file, $this->attachmentsPath); // make sure the file extension is supported if (!VikBooking::isFileTypeCompatible($attachment->getExtension(), $this->supportedFiles)) { throw new \RuntimeException('File type not supported: ' . $attachment->getExtension(), 400); } if (!$attachment->upload()) { throw new \RuntimeException('Impossible to upload the file: ' . $attachment->getName(), 403); } return $attachment; } /** * Removes the specified attachment from the server. * * @param VBOChatAttachment $attachment * * @return bool */ public function removeAttachment(VBOChatAttachment $attachment) { if (!$attachment->exists()) { return false; } return JFile::delete($attachment->getPath()); } /** * Reads all the messages under the specified context for the currently logged in user. * * @param VBOChatContext $context The chat context. * @param string|null $date When specified, only the messages with creation date equal or * lower than this value will be read. * * @return int[] A list of read message IDs. */ public function readMessages(VBOChatContext $context, ?string $date = null) { $search = (new VBOChatSearch) // take the latest 50 unread messages ->start(0)->limit(50)->unread() // under the specified context ->withContext($context) // created before the specified threshold date ->date($date ?: JFactory::getDate('now')->toSql(), '<=') // unread by the currently logged in user ->reader($this->getUser()->getID()); $read = []; // iterate all unread messages foreach ($this->storage->getMessages($search) as $message) { try { // read the message $this->storage->readMessage($message->id, $this->getUser()->getID()); // mark message as read $read[] = $message->id; } catch (Exception $error) { // go ahead silently } } return $read; } /** * Creates a new message object. * * @param object|array $message The raw message record. * * @return VBOChatMessage */ public function createMessage($message) { if (!is_array($message) && !is_object($message)) { throw new \InvalidArgumentException('Cannot bind chat message! Array or object expected, ' . gettype($message) . ' given.', 400); } $message = (object) $message; // hold raw data into a message object return new VBOChatMessage( // create proper context handler $this->createContext($message->context ?? '', $message->id_context ?? 0), // bind raw information $message ); } /** * Creates a new context object. * * @param string $alias The context alias identifier. * @param int $id The context foreign key. * * @return VBOChatContext */ public function createContext(string $alias, int $id) { if ($alias === '') { throw new InvalidArgumentException('The context alias cannot be empty.', 400); } if ($id <= 0) { throw new InvalidArgumentException('Invalid context foreign key provided.', 400); } // build context class name $classname = 'VBOChatContext' . ucfirst(strtolower($alias)); if (!class_exists($classname)) { throw new RuntimeException('The class [' . $classname . '] does not exist.', 404); } // instantiate class by inject the provided ID return new $classname($id); } /** * Forces the pre-loading of the resources to make the chat scripts work. * * @return self */ public function useAssets() { static $loaded = false; if ($loaded) { // do not load assets again return $this; } $loaded = true; // load dependencies first JHtml::fetch('jquery.framework'); VikBooking::getVboApplication()->loadContextMenuAssets(); // make translations available also for JS scripts JText::script('VBO_CHAT_YOU'); JText::script('VBTODAY'); JText::script('VBOYESTERDAY'); JText::script('VBO_CHAT_SENDING_ERR'); JText::script('VBO_CHAT_TEXTAREA_PLACEHOLDER'); JText::script('VBO_ATTACH'); $document = JFactory::getDocument(); $document->addScript(VBO_SITE_URI . 'resources/chat.js'); $document->addStyleSheet(VBO_SITE_URI . 'resources/chat.css'); // load assets for each supported context (new VBOChatContextTask(0))->useAssets(); return $this; } /** * Renders the chat interface. * * @param VBOChatContext $context The conversation context. * @param array $options A configuration array. * * List of supported configuration options. * @var bool assets Whether the resources should be loaded (true by default). * * @return string The chat interface output. */ public function render(VBOChatContext $context, array $options = []) { if ($options['assets'] ?? true) { $this->useAssets(); } // load the latest 20 messages of the specified context $messages = $this->getMessages( (new VBOChatSearch)->limit($options['limit'] ?? 20)->withContext($context) ); $users = []; // get all involved users foreach ($context->getRecipients() as $recipient) { $users[$recipient->getID()] = $recipient; } // detect AJAX base URI environment depending on the platform $ajaxUri = VBOFactory::getPlatform()->getUri()->ajax('index.php?option=com_vikbooking'); // generate a random suffix in case it has been specified $options['suffix'] = $options['suffix'] ?? uniqid(); // create layout file $layout = new JLayoutFile('chat.chat', null, [ 'component' => 'com_vikbooking', 'client' => 'admin', ]); // render template return $layout->render([ 'uri' => $ajaxUri, 'messages' => $messages, 'users' => $users, 'user' => $this->getUser(), 'options' => $options, 'context' => [ 'id' => $context->getID(), 'alias' => $context->getAlias(), 'actions' => $context->getActions(), ], ]); } }