Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -38,87 +38,90 @@ public function fetchUserState()
$user = $this->app->getIdentity();

$tourId = $this->app->input->getInt('tid', 0);
$stepNumber = $this->app->input->getString('sid', '');
$stepNumber = $this->app->input->getInt('sid', 0);
$context = $this->app->input->getString('context', '');

if ($user != null && $user->id > 0) {
$actionState = '';

switch ($context) {
case 'tour.complete':
$actionState = 'completed';
break;
case 'tour.cancel':
$actionState = 'delayed';
break;
case 'tour.skip':
$actionState = 'skipped';
break;
}
if ($user == null || $user->id <= 0) {
echo new JsonResponse(['success' => false, 'tourId' => $tourId], Text::_('COM_GUIDEDTOURS_USERSTATE_CONNECTEDONLY'), true);
$this->app->close();
}

if (!\in_array($context, ['tour.complete', 'tour.cancel', 'tour.skip'])) {
echo new JsonResponse(['success' => false, 'tourId' => $tourId], Text::_('COM_GUIDEDTOURS_USERSTATE_WRONGCONTEXT'), true);
$this->app->close();
}

PluginHelper::importPlugin('guidedtours');

// event onBeforeTourSaveUserState before save user tour state
$beforeEvent = AbstractEvent::create(
'onBeforeTourSaveUserState',
[
'subject' => new \stdClass(),
'tourId' => $tourId,
'actionState' => $actionState,
'stepNumber' => $stepNumber,
]
);

$this->app->getDispatcher()->dispatch('onBeforeTourSaveUserState', $beforeEvent);

// Save the tour state only when the tour auto-starts.
$tourModel = $this->getModel('Tour', 'Administrator');
if ($tourModel->isAutostart($tourId)) {
$result = $tourModel->saveTourUserState($tourId, $actionState);
if ($result) {
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATESAVED', $user->id, $tourId);
} else {
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED', $user->id, $tourId);
}
if ($tourId <= 0) {
echo new JsonResponse(['success' => false, 'tourId' => $tourId], Text::_('COM_GUIDEDTOURS_USERSTATE_BADTOURID'), true);
$this->app->close();
}

$actionState = '';

switch ($context) {
case 'tour.complete':
$actionState = 'completed';
break;
case 'tour.cancel':
$actionState = 'delayed';
break;
case 'tour.skip':
$actionState = 'skipped';
break;
}

PluginHelper::importPlugin('guidedtours');

// event onBeforeTourSaveUserState before save user tour state
$beforeEvent = AbstractEvent::create(
'onBeforeTourSaveUserState',
[
'subject' => new \stdClass(),
'tourId' => $tourId,
'actionState' => $actionState,
'stepNumber' => $stepNumber,
]
);

$this->app->getDispatcher()->dispatch('onBeforeTourSaveUserState', $beforeEvent);

// Save the tour state only when the tour auto-starts.
$tourModel = $this->getModel('Tour', 'Administrator');
if ($tourModel->isAutostart($tourId)) {
$result = $tourModel->saveTourUserState($tourId, $actionState);
if ($result) {
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATESAVED', $user->id, $tourId);
} else {
$result = false;
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED', $user->id, $tourId);
}

// event onAfterTourSaveUserState after save user tour state (may override message)
$afterEvent = AbstractEvent::create(
'onAfterTourSaveUserState',
[
'subject' => new \stdClass(),
'tourId' => $tourId,
'actionState' => $actionState,
'stepNumber' => $stepNumber,
'result' => $result,
'message' => &$message,
]
);

$this->app->getDispatcher()->dispatch('onAfterTourSaveUserState', $afterEvent);

// Construct the response data
$data = [
'tourId' => $tourId,
'stepId' => $stepNumber,
'context' => $context,
'state' => $actionState,
];
echo new JsonResponse($data, $message);
$this->app->close();
} else {
// Construct the response data
$data = [
'success' => false,
'tourId' => $tourId,
];

$message = Text::_('COM_GUIDEDTOURS_USERSTATE_CONNECTEDONLY');
echo new JsonResponse($data, $message, true);
$this->app->close();
$result = false;
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED', $user->id, $tourId);
}

// event onAfterTourSaveUserState after save user tour state (may override message)
$afterEvent = AbstractEvent::create(
'onAfterTourSaveUserState',
[
'subject' => new \stdClass(),
'tourId' => $tourId,
'actionState' => $actionState,
'stepNumber' => $stepNumber,
'result' => $result,
'message' => &$message,
]
);

$this->app->getDispatcher()->dispatch('onAfterTourSaveUserState', $afterEvent);

// Construct the response data
$data = [
'tourId' => $tourId,
'stepId' => $stepNumber,
'context' => $context,
'state' => $actionState,
];
echo new JsonResponse($data, $message);
$this->app->close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/**
* @package Joomla.Administrator
* @subpackage com_guidedtours
*
* @copyright (C) 2024 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\Component\Guidedtours\Administrator\Dispatcher;

use Joomla\CMS\Access\Exception\NotAllowed;
use Joomla\CMS\Dispatcher\ComponentDispatcher;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
* ComponentDispatcher class for com_guidedtours
*
* @since __DEPLOY_VERSION__
*/
class Dispatcher extends ComponentDispatcher
{
/**
* Method to check component access permission
*
* @return void
*
* @throws \Exception|NotAllowed
*/
protected function checkAccess()
{
$command = $this->input->getCmd('task', 'display');
if ($this->app->isClient('administrator') && $command == 'ajax.fetchUserState') {
return;
}

parent::checkAccess();
}
}
2 changes: 2 additions & 0 deletions administrator/language/en-GB/com_guidedtours.ini
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,10 @@ COM_GUIDEDTOURS_TYPE_REDIRECT_URL_DESC="Enter the relative URL of the page you w
COM_GUIDEDTOURS_TYPE_REDIRECT_URL_LABEL="Relative URL"
COM_GUIDEDTOURS_URL_LABEL="Relative URL"
COM_GUIDEDTOURS_URL_DESC="Enter the relative URL of the page from where you want to Start the tour, e.g administrator/index.php?option=com_guidedtours&view=tours for the tours' list page."
COM_GUIDEDTOURS_USERSTATE_BADTOURID="The Tour needs to have a positive id."
; 'Tour User states' are conditions in which a guided tour user leaves a tour, either by cancelling, completing or skipping a tour.
COM_GUIDEDTOURS_USERSTATE_CONNECTEDONLY="The recording of User states for a tour is only available to logged-in users."
COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED="The User state was not saved for user %1$s tour %2$s."
COM_GUIDEDTOURS_USERSTATE_STATESAVED="The User state was saved for user %1$s tour %2$s."
COM_GUIDEDTOURS_USERSTATE_WRONGCONTEXT="The Tour User state context is incorrect."
COM_GUIDEDTOURS_XML_DESCRIPTION="Component for managing Guided Tours functionality."