PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
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 1.7.4 All 35 releases
vikbooking / admin / controllers / chat.php

chat.php in VikBooking Hotel Booking Engine & PMS trunk, at admin/controllers/chat.php

510 lines 16.3 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) 2025 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 * VikBooking generic chat controller.
16 *
17 * @since 1.8
18 */
19 class VikBookingControllerChat extends JControllerAdmin
20 {
21 /**
22 * Task used to render the chat asynchronously.
23 *
24 * @return void
25 */
26 public function render_chat()
27 {
28 $app = JFactory::getApplication();
29
30 if (!JSession::checkToken()) {
31 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
32 }
33
34 // fetch request data
35 $contextId = $app->input->getUint('id_context', 0);
36 $context = $app->input->get('context', '');
37
38 try {
39 /** @var VBOChatMediator */
40 $chat = VBOFactory::getChatMediator();
41
42 /** @var VBOChatContext */
43 $context = $chat->createContext($context, $contextId);
44
45 // render the chat
46 $html = $chat->render($context, [
47 'assets' => false,
48 ]);
49 } catch (Exception $error) {
50 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
51 }
52
53 // output the result content
54 VBOHttpDocument::getInstance($app)->json(['html' => $html]);
55 }
56
57 /**
58 * Task used to periodically search for new messages under a given context.
59 *
60 * @return void
61 */
62 public function sync_messages()
63 {
64 $app = JFactory::getApplication();
65
66 if (!JSession::checkToken()) {
67 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
68 }
69
70 // fetch request data
71 $contextId = $app->input->getUint('id_context', 0);
72 $context = $app->input->get('context', '');
73 $threshold = $app->input->getUint('threshold', 0);
74
75 try {
76 /** @var VBOChatMediator */
77 $chat = VBOFactory::getChatMediator();
78
79 /** @var VBOChatContext */
80 $context = $chat->createContext($context, $contextId);
81
82 // obtain all the messages under the created context with an ID higher than the specified threshold
83 $messages = $chat->getMessages(
84 (new VBOChatSearch)
85 ->withContext($context)
86 ->message($threshold, '>')
87 );
88
89 // maintain the user logged in
90 $chat->getUser()->keepAlive($context);
91
92 // obtain the public context metadata
93 $metadata = $context->getMetadata($public = true);
94 } catch (Exception $error) {
95 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
96 }
97
98 // output the result content
99 VBOHttpDocument::getInstance($app)->json([
100 'messages' => $messages,
101 'metadata' => $metadata,
102 ]);
103 }
104
105 /**
106 * Task used to scan the pagination of a specified chat.
107 *
108 * @return void
109 */
110 public function load_older_messages()
111 {
112 $app = JFactory::getApplication();
113
114 if (!JSession::checkToken()) {
115 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
116 }
117
118 // fetch request data
119 $contextId = $app->input->getUint('id_context', 0);
120 $context = $app->input->get('context', '');
121 $start = $app->input->getUint('start', 0);
122 $limit = $app->input->getUint('limit', 20);
123 $datetime = $app->input->getString('datetime', null);
124
125 try {
126 /** @var VBOChatMediator */
127 $chat = VBOFactory::getChatMediator();
128
129 /** @var VBOChatContext */
130 $context = $chat->createContext($context, $contextId);
131
132 // obtain all the messages under the created context with a creation date equal or lower than the specified threshold
133 $messages = $chat->getMessages(
134 (new VBOChatSearch)
135 ->start($start)
136 ->limit($limit)
137 ->withContext($context)
138 ->date($datetime, '<=')
139 );
140 } catch (Exception $error) {
141 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
142 }
143
144 // output the result content
145 VBOHttpDocument::getInstance($app)->json($messages);
146 }
147
148 /**
149 * Task used to send a chat message under a given context.
150 *
151 * @return void
152 */
153 public function send()
154 {
155 $app = JFactory::getApplication();
156
157 if (!JSession::checkToken()) {
158 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
159 }
160
161 // fetch request data
162 $contextId = $app->input->getUint('id_context', 0);
163 $context = $app->input->get('context', '');
164 $message = $app->input->getString('message', '');
165 $createdon = $app->input->getString('createdon', '');
166 $attachments = $app->input->get('attachments', [], 'array');
167
168 try {
169 /** @var VBOChatMediator */
170 $chat = VBOFactory::getChatMediator();
171
172 foreach ($attachments as &$attachment) {
173 // create attachment from JSON
174 $attachment = new VBOChatAttachment($attachment);
175 }
176
177 /** @var VBOChatMessage */
178 $message = $chat->createMessage([
179 'context' => $context,
180 'id_context' => $contextId,
181 'message' => $message,
182 'attachments' => $attachments,
183 'createdon' => $createdon,
184 ]);
185
186 // deliver the message
187 $chat->send($message);
188 } catch (Exception $error) {
189 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
190 }
191
192 // output the result content
193 VBOHttpDocument::getInstance($app)->json($message);
194 }
195
196 /**
197 * AJAX end-point used to upload attachments before sending the message.
198 * Files are uploaded onto the attachments folder of the front-end and a
199 * JSON encoded objects array is returned with the details of each file.
200 *
201 * @return void
202 */
203 public function upload_attachments()
204 {
205 $app = JFactory::getApplication();
206
207 if (!JSession::checkToken()) {
208 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
209 }
210
211 // fetch request data
212 $files = $app->input->files->get('attachments', [], 'raw');
213
214 if (isset($files['name'])) {
215 // we have a single associative array, we need to push it within a list,
216 // because the upload iterates the $files array
217 $files = [$files];
218 }
219
220 $attachments = [];
221
222 try {
223 /** @var VBOChatMediator */
224 $chat = VBOFactory::getChatMediator();
225
226 foreach ($files as $file) {
227 /** @var VBOChatAttachment */
228 $attachment = $chat->uploadAttachment($file);
229
230 // register attachment within the list
231 $attachments[] = $attachment;
232 }
233 } catch (Exception $error) {
234 // iterate all uploaded attachments and unlink them
235 foreach ($attachments as $attachment) {
236 $chat->removeAttachment($attachment);
237 }
238
239 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
240 }
241
242 // output the result content
243 VBOHttpDocument::getInstance($app)->json($attachments);
244 }
245
246 /**
247 * AJAX end-point used to remove the selected attachment.
248 *
249 * @return void
250 */
251 public function remove_attachment()
252 {
253 $app = JFactory::getApplication();
254
255 if (!JSession::checkToken()) {
256 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
257 }
258
259 // fetch request data
260 $file = $app->input->get('attachment', [], 'array');
261
262 try {
263 /** @var VBOChatMediator */
264 $chat = VBOFactory::getChatMediator();
265
266 // create attachment
267 $attachment = new VBOChatAttachment($file);
268
269 // attempt to remove the attachment
270 if (!$chat->removeAttachment($attachment)) {
271 throw new RuntimeException('Unable to remove the attachment: ' . $attachment->getName(), 403);
272 }
273 } catch (Exception $error) {
274 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
275 }
276
277 $app->close();
278 }
279
280 /**
281 * Task used to read the unread messages of a specified chat.
282 *
283 * @return void
284 */
285 public function read_messages()
286 {
287 $app = JFactory::getApplication();
288
289 if (!JSession::checkToken()) {
290 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
291 }
292
293 // fetch request data
294 $contextId = $app->input->getUint('id_context', 0);
295 $context = $app->input->get('context', '');
296 $datetime = $app->input->getString('datetime', null);
297
298 try {
299 /** @var VBOChatMediator */
300 $chat = VBOFactory::getChatMediator();
301
302 /** @var VBOChatContext */
303 $context = $chat->createContext($context, $contextId);
304
305 // obtain a list holding the ID of all the read messages
306 $messages = $chat->readMessages($context, $datetime);
307 } catch (Exception $error) {
308 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
309 }
310
311 // output the result content
312 VBOHttpDocument::getInstance($app)->json($messages);
313 }
314
315 /**
316 * Task used to scan the pagination of all the existing chats.
317 *
318 * @return void
319 */
320 public function load_chats()
321 {
322 $app = JFactory::getApplication();
323
324 if (!JSession::checkToken()) {
325 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
326 }
327
328 // fetch request data
329 $start = $app->input->getUint('start', 0);
330 $limit = $app->input->getUint('limit', 20);
331 $categories = $app->input->getString('categories', []);
332 $options = $app->input->get('options', [], 'array');
333
334 try {
335 /** @var VBOChatMediator */
336 $chat = VBOFactory::getChatMediator();
337
338 // obtain all the chats
339 $messages = $chat->getMessages(
340 (new VBOChatSearch)
341 ->forCategories($categories)
342 ->start($start)
343 ->limit($limit)
344 ->aggregate()
345 );
346 } catch (Exception $error) {
347 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
348 }
349
350 $layoutFile = new JLayoutFile('chat.threads.thread');
351
352 $chats = [];
353
354 // create thread layout here
355 foreach ($messages as $thread) {
356 $chats[] = [
357 'alias' => $thread->getContext()->getAlias(),
358 'id' => $thread->getContext()->getID(),
359 'html' => $layoutFile->render([
360 'thread' => $thread,
361 'options' => $options,
362 ]),
363 ];
364 }
365
366 // output the result content
367 VBOHttpDocument::getInstance($app)->json($chats);
368 }
369
370 /**
371 * Updates the metadata for the specified context.
372 *
373 * @return void
374 *
375 * @since 1.8.8
376 */
377 public function update_metadata()
378 {
379 $app = JFactory::getApplication();
380
381 // fetch request data
382 $contextId = $app->input->getUint('id_context', 0);
383 $context = $app->input->get('context', '');
384 $key = $app->input->getString('key');
385 $val = $app->input->getString('val', null);
386
387 try {
388 if (!JSession::checkToken()) {
389 throw new RuntimeException(JText::translate('JINVALID_TOKEN'), 403);
390 }
391
392 /** @var VBOChatMediator */
393 $chat = VBOFactory::getChatMediator();
394
395 /** @var VBOChatContext */
396 $context = $chat->createContext($context, $contextId);
397
398 // update the metadata
399 $context->setMetadata($key, $val);
400 } catch (Exception $error) {
401 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage());
402 }
403
404 VBOHttpDocument::getInstance($app)->json((new VBOChatSessionModel)->getItem($sessionId));
405
406 $app->close();
407 }
408
409 /**
410 * AJAX endpoint to send a message template to a chat session.
411 *
412 * NOTE: VikChannelManager and WhatsApp channel are required.
413 *
414 * @return void
415 */
416 public function send_template()
417 {
418 $app = JFactory::getApplication();
419
420 try {
421 if (!JSession::checkToken()) {
422 // missing CSRF-proof token
423 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
424 }
425
426 // gather request values
427 $sessionId = $app->input->getUint('session_id', '');
428 $configId = $app->input->getUInt('config_id', 0);
429
430 // recover session details from ID
431 $session = (new VBOChatSessionModel)->getItem($sessionId);
432
433 if (!$session) {
434 throw new DomainException("Session [$sessionId] not found.", 404);
435 }
436
437 // get WhatsApp business account ID from session metadata
438 $accountId = $session->metadata['waba_id'];
439
440 // account validation
441 if (empty($accountId)) {
442 throw new RuntimeException('Missing account or phone ID.', 400);
443 }
444
445 // find account record
446 $accountRecord = VCMMessagingAccountsModel::getInstance()->getItem([
447 'idchannel' => VikChannelManagerConfig::WHATSAPP,
448 'account_id' => $accountId,
449 ]);
450
451 if (!$accountRecord) {
452 throw new RuntimeException('Could not find WhatsApp Business Account data.', 404);
453 }
454
455 if (empty($accountRecord->settings['configurations'][$configId])) {
456 throw new RuntimeException('Could not find account configuration data.', 404);
457 }
458
459 if (empty($session->phone)) {
460 throw new RuntimeException('Missing recipient phone number.', 400);
461 }
462
463 // build template decorator
464 if (!empty($session->metadata['quote_id'])) {
465 // use the quote decorator
466 $tplDecorator = new VCMMessagingTemplateDecoratorQuote((int) $session->metadata['quote_id']);
467 } else {
468 // default to chat decorator
469 $tplDecorator = new VCMMessagingTemplateDecoratorChat($session);
470 }
471
472 // send message template
473 $sendResult = (new VCMWhatsappModelService($accountRecord->account_id, $accountRecord->phone_id))
474 ->sendTemplate(
475 $session->phone,
476 $accountRecord,
477 $configId,
478 [
479 'decorator' => $tplDecorator,
480 ]
481 );
482
483 /** @var VBOChatMediator */
484 $chat = VBOFactory::getChatMediator();
485
486 /** @var VBOChatUser */
487 $user = $chat->getUser();
488
489 /** @var VBOChatMessage */
490 $message = $chat->createMessage([
491 'context' => 'session',
492 'id_context' => $sessionId,
493 'sender_name' => $user->getName(),
494 'id_sender' => $user->getID(),
495 'message' => $sendResult->text,
496 'ref_id' => $sendResult->id,
497 ]);
498
499 // collect template message without sending any notification
500 $chat->saveMessage($message);
501
502 } catch (Exception $error) {
503 VBOHttpDocument::getInstance($app)->close($error->getCode() ?: 500, $error->getMessage() ?: 'Error');
504 }
505
506 // output requested message template details
507 VBOHttpDocument::getInstance($app)->json($sendResult);
508 }
509 }
510