Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.2] [Guided Tours] Fix permission errors when recording user tour state #44082

Merged
merged 11 commits into from
Sep 18, 2024
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,7 +94,9 @@ 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."
COM_GUIDEDTOURS_USERSTATE_CONNECTEDONLY="Tour User state action is only for connected users."
COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED="Tour User state not saved for user %1$s tour %2$s."
COM_GUIDEDTOURS_USERSTATE_STATESAVED="Tour User state saved for user %1$s tour %2$s."
COM_GUIDEDTOURS_USERSTATE_WRONGCONTEXT="The Tour User State context is incorrect."
obuisard marked this conversation as resolved.
Show resolved Hide resolved
COM_GUIDEDTOURS_XML_DESCRIPTION="Component for managing Guided Tours functionality."