PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.6
VikBooking Hotel Booking Engine & PMS v1.8.6
1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 All 36 releases
vikbooking / admin / helpers / src / chat / mediator.php

mediator.php in VikBooking Hotel Booking Engine & PMS 1.8.6, at admin/helpers/src/chat/mediator.php

414 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 /**
15 * Chat mediator class.
16 *
17 * @since 1.8
18 */
19 class VBOChatMediator
20 {
21 /**
22 * The storage engine used for input/output purposes.
23 *
24 * @var VBOChatStorage
25 */
26 protected $storage;
27
28 /**
29 * The currently authenticated user.
30 *
31 * @var VBOChatUser
32 */
33 private $user;
34
35 /**
36 * The path where the attachments are internally stored.
37 *
38 * @var string
39 */
40 protected $attachmentsPath;
41
42 /**
43 * A string holding all the supported file extensions, separated by a comma.
44 *
45 * @var string
46 */
47 protected $supportedFiles;
48
49 /**
50 * Class constructor.
51 *
52 * @param VBOChatStorage $storage
53 */
54 public function __construct(VBOChatStorage $storage)
55 {
56 $this->storage = $storage;
57
58 // create default attachments folder
59 $this->attachmentsPath = (defined('VBO_MEDIA_PATH') ? VBO_MEDIA_PATH : '') . DIRECTORY_SEPARATOR . 'attachments';
60
61 // create default attachments extension filters
62 $this->supportedFiles = implode(',', [
63 // images
64 'png,apng,bmp,gif,ico,jpg,jpeg,svg,heic,webp',
65 // videos
66 'mp4,mov,ogm,webm,3gp,asf,avi,divx,flv,mkv,mpg,mpeg,wmv,xvid',
67 // audios
68 'aac,m4a,mp3,opus,wav,wave,ac3,aiff,flac,mid,midi,wma',
69 // archives
70 'zip,tar,rar,gz,bzip2',
71 // documents
72 'pdf,doc,docx,rtf,odt,pages,txt,md,markdown',
73 // spreedsheets
74 'xls,xlsx,csv,ods,numbers',
75 // presentations
76 'pps,ppsx,odp,keynote',
77 ]);
78 }
79
80 /**
81 * Authenticates as the provided user.
82 * When no user is passed, the system will attempt to auto-login according
83 * to the client and session data.
84 *
85 * @param VBOChatUser|null $user
86 *
87 * @return self
88 */
89 public function authenticate(?VBOChatUser $user = null)
90 {
91 if ($user === null) {
92 // auto-bind the sender only if not provided
93 if (JFactory::getApplication()->isClient('administrator')) {
94 // authenticate as administrator
95 $user = new VBOChatUserAdmin;
96 } else {
97 // authenticate as operator
98 $user = new VBOChatUserOperator;
99 }
100 }
101
102 $this->user = $user;
103
104 return $this;
105 }
106
107 /**
108 * Returns the currently logged in user.
109 * In case of missing authentication, the system will attempt to
110 * perform an auto-login.
111 *
112 * @return VBOChatUser
113 */
114 public function getUser()
115 {
116 if (!$this->user) {
117 // no authenticated user, auto-login now
118 $this->authenticate();
119 }
120
121 return $this->user;
122 }
123
124 /**
125 * Returns the messages matching the specified search query.
126 *
127 * @param VBOChatSearch $search
128 *
129 * @return VBOChatMessage[]
130 */
131 public function getMessages(VBOChatSearch $search)
132 {
133 $messages = [];
134
135 if (!$search->hasReader()) {
136 // forces the current user as the reader
137 $search->reader($this->getUser()->getID());
138 }
139
140 // pull the messages from the storage
141 $rows = $this->storage->getMessages($search);
142
143 foreach ($rows as $raw) {
144 // wrap raw record within a message object
145 $messages[] = $this->createMessage($raw);
146 }
147
148 return $messages;
149 }
150
151 /**
152 * Sends a new message to all the recipients of the context.
153 *
154 * @param VBOChatMessage $message
155 *
156 * @return void
157 */
158 public function send(VBOChatMessage $message)
159 {
160 $user = $this->getUser();
161
162 // force the sender name and ID according to the details of the logged in user
163 $message->setSender($user->getName(), $user->getID());
164
165 // attempt to save the message
166 $this->storage->saveMessage($message);
167
168 // iterate all the users that should receive a notification
169 foreach ($message->getContext()->getRecipients() as $recipient) {
170 if ($message->getSenderID() == $recipient->getID()) {
171 // do not notify myself
172 continue;
173 }
174
175 if ($recipient instanceof VBOChatNotifiable) {
176 // schedule message notification
177 $recipient->scheduleNotification($message, $user);
178 }
179 }
180 }
181
182 /**
183 * Moves the uploaded temporary file onto the server and creates a new attachment.
184 *
185 * @param array $file The temporary file under $_FILES.
186 *
187 * @return VBOChatAttachment
188 *
189 * @throws Exception
190 */
191 public function uploadAttachment(array $file)
192 {
193 // assert attachments folder first
194 if (!JFolder::exists($this->attachmentsPath) && !JFolder::create($this->attachmentsPath)) {
195 throw new \RuntimeException('Unable to create the attachments folder: ' . $this->attachmentsPath, 403);
196 }
197
198 // create upload attachment
199 $attachment = new VBOChatAttachmentUpload($file, $this->attachmentsPath);
200
201 // make sure the file extension is supported
202 if (!VikBooking::isFileTypeCompatible($attachment->getExtension(), $this->supportedFiles)) {
203 throw new \RuntimeException('File type not supported: ' . $attachment->getExtension(), 400);
204 }
205
206 if (!$attachment->upload()) {
207 throw new \RuntimeException('Impossible to upload the file: ' . $attachment->getName(), 403);
208 }
209
210 return $attachment;
211 }
212
213 /**
214 * Removes the specified attachment from the server.
215 *
216 * @param VBOChatAttachment $attachment
217 *
218 * @return bool
219 */
220 public function removeAttachment(VBOChatAttachment $attachment)
221 {
222 if (!$attachment->exists()) {
223 return false;
224 }
225
226 return JFile::delete($attachment->getPath());
227 }
228
229 /**
230 * Reads all the messages under the specified context for the currently logged in user.
231 *
232 * @param VBOChatContext $context The chat context.
233 * @param string|null $date When specified, only the messages with creation date equal or
234 * lower than this value will be read.
235 *
236 * @return int[] A list of read message IDs.
237 */
238 public function readMessages(VBOChatContext $context, ?string $date = null) {
239 $search = (new VBOChatSearch)
240 // take the latest 50 unread messages
241 ->start(0)->limit(50)->unread()
242 // under the specified context
243 ->withContext($context)
244 // created before the specified threshold date
245 ->date($date ?: JFactory::getDate('now')->toSql(), '<=')
246 // unread by the currently logged in user
247 ->reader($this->getUser()->getID());
248
249 $read = [];
250
251 // iterate all unread messages
252 foreach ($this->storage->getMessages($search) as $message) {
253 try {
254 // read the message
255 $this->storage->readMessage($message->id, $this->getUser()->getID());
256
257 // mark message as read
258 $read[] = $message->id;
259 } catch (Exception $error) {
260 // go ahead silently
261 }
262 }
263
264 return $read;
265 }
266
267 /**
268 * Creates a new message object.
269 *
270 * @param object|array $message The raw message record.
271 *
272 * @return VBOChatMessage
273 */
274 public function createMessage($message)
275 {
276 if (!is_array($message) && !is_object($message)) {
277 throw new \InvalidArgumentException('Cannot bind chat message! Array or object expected, ' . gettype($message) . ' given.', 400);
278 }
279
280 $message = (object) $message;
281
282 // hold raw data into a message object
283 return new VBOChatMessage(
284 // create proper context handler
285 $this->createContext($message->context ?? '', $message->id_context ?? 0),
286 // bind raw information
287 $message
288 );
289 }
290
291 /**
292 * Creates a new context object.
293 *
294 * @param string $alias The context alias identifier.
295 * @param int $id The context foreign key.
296 *
297 * @return VBOChatContext
298 */
299 public function createContext(string $alias, int $id)
300 {
301 if ($alias === '') {
302 throw new InvalidArgumentException('The context alias cannot be empty.', 400);
303 }
304
305 if ($id <= 0) {
306 throw new InvalidArgumentException('Invalid context foreign key provided.', 400);
307 }
308
309 // build context class name
310 $classname = 'VBOChatContext' . ucfirst(strtolower($alias));
311
312 if (!class_exists($classname)) {
313 throw new RuntimeException('The class [' . $classname . '] does not exist.', 404);
314 }
315
316 // instantiate class by inject the provided ID
317 return new $classname($id);
318 }
319
320 /**
321 * Forces the pre-loading of the resources to make the chat scripts work.
322 *
323 * @return self
324 */
325 public function useAssets()
326 {
327 static $loaded = false;
328
329 if ($loaded) {
330 // do not load assets again
331 return $this;
332 }
333
334 $loaded = true;
335
336 // load dependencies first
337 JHtml::fetch('jquery.framework');
338 VikBooking::getVboApplication()->loadContextMenuAssets();
339
340 // make translations available also for JS scripts
341 JText::script('VBO_CHAT_YOU');
342 JText::script('VBTODAY');
343 JText::script('VBOYESTERDAY');
344 JText::script('VBO_CHAT_SENDING_ERR');
345 JText::script('VBO_CHAT_TEXTAREA_PLACEHOLDER');
346 JText::script('VBO_ATTACH');
347
348 $document = JFactory::getDocument();
349 $document->addScript(VBO_SITE_URI . 'resources/chat.js');
350 $document->addStyleSheet(VBO_SITE_URI . 'resources/chat.css');
351
352 // load assets for each supported context
353 (new VBOChatContextTask(0))->useAssets();
354
355 return $this;
356 }
357
358 /**
359 * Renders the chat interface.
360 *
361 * @param VBOChatContext $context The conversation context.
362 * @param array $options A configuration array.
363 *
364 * List of supported configuration options.
365 * @var bool assets Whether the resources should be loaded (true by default).
366 *
367 * @return string The chat interface output.
368 */
369 public function render(VBOChatContext $context, array $options = [])
370 {
371 if ($options['assets'] ?? true) {
372 $this->useAssets();
373 }
374
375 // load the latest 20 messages of the specified context
376 $messages = $this->getMessages(
377 (new VBOChatSearch)->limit($options['limit'] ?? 20)->withContext($context)
378 );
379
380 $users = [];
381
382 // get all involved users
383 foreach ($context->getRecipients() as $recipient) {
384 $users[$recipient->getID()] = $recipient;
385 }
386
387 // detect AJAX base URI environment depending on the platform
388 $ajaxUri = VBOFactory::getPlatform()->getUri()->ajax('index.php?option=com_vikbooking');
389
390 // generate a random suffix in case it has been specified
391 $options['suffix'] = $options['suffix'] ?? uniqid();
392
393 // create layout file
394 $layout = new JLayoutFile('chat.chat', null, [
395 'component' => 'com_vikbooking',
396 'client' => 'admin',
397 ]);
398
399 // render template
400 return $layout->render([
401 'uri' => $ajaxUri,
402 'messages' => $messages,
403 'users' => $users,
404 'user' => $this->getUser(),
405 'options' => $options,
406 'context' => [
407 'id' => $context->getID(),
408 'alias' => $context->getAlias(),
409 'actions' => $context->getActions(),
410 ],
411 ]);
412 }
413 }
414