File "mediator.php"
Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/helpers/src/chat/mediator.php
File size: 12.07 KB
MIME-type: text/x-php
Charset: utf-8
<?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(),
],
]);
}
}