Skip to content

Commit 76252d6

Browse files
committed
Merge remote-tracking branch 'upstream/5.2-dev' into 5.3/upmerge
2 parents 0b7b8ad + a79cd92 commit 76252d6

File tree

71 files changed

+2951
-478
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+2951
-478
lines changed

administrator/components/com_actionlogs/config.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
label="COM_ACTIONLOGS_LOG_EXTENSIONS_LABEL"
3131
multiple="true"
3232
layout="joomla.form.field.list-fancy-select"
33-
default="com_banners,com_cache,com_categories,com_checkin,com_config,com_contact,com_content,com_fields,com_installer,com_media,com_menus,com_messages,com_modules,com_newsfeeds,com_plugins,com_redirect,com_scheduler,com_tags,com_templates,com_users"
33+
default="com_banners,com_cache,com_categories,com_checkin,com_config,com_contact,com_content,com_fields,com_guidedtours,com_installer,com_media,com_menus,com_messages,com_modules,com_newsfeeds,com_plugins,com_redirect,com_scheduler,com_tags,com_templates,com_users"
3434
/>
3535
<field
3636
name="loggable_api"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- Add mod_articles module
2+
INSERT INTO `#__extensions` (`package_id`, `name`, `type`, `element`, `folder`, `client_id`, `enabled`, `access`, `protected`, `locked`, `manifest_cache`, `params`, `custom_data`) VALUES
3+
(0, 'mod_articles', 'module', 'mod_articles', '', 0, 1, 0, 0, 1, '', '', '');
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
--
2+
-- Add the Guided Tours selectable option to the User Action Logs
3+
--
4+
INSERT INTO `#__action_logs_extensions` (`extension`) VALUES ('com_guidedtours');
5+
6+
INSERT INTO `#__action_log_config` (`type_title`, `type_alias`, `id_holder`, `title_holder`, `table_name`, `text_prefix`) VALUES
7+
('guidedtour', 'com_guidedtours.state', 'id', 'title', '#__guidedtours', 'PLG_ACTIONLOG_JOOMLA');
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- Add mod_articles module
2+
INSERT INTO "#__extensions" ("package_id", "name", "type", "element", "folder", "client_id", "enabled", "access", "protected", "locked", "manifest_cache", "params", "custom_data", "ordering", "state") VALUES
3+
(0, 'mod_articles', 'module', 'mod_articles', '', 0, 1, 0, 0, 1, '', '', '', 0, 0);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
--
2+
-- Add the Guided Tours selectable option to the User Action Logs
3+
--
4+
INSERT INTO "#__action_logs_extensions" ("extension") VALUES ("com_guidedtours");
5+
6+
INSERT INTO "#__action_log_config" ("type_title", "type_alias", "id_holder", "title_holder", "table_name", "text_prefix") VALUES
7+
('guidedtour', 'com_guidedtours.state', 'id', 'title', '#__guidedtours', 'PLG_ACTIONLOG_JOOMLA');

administrator/components/com_content/src/Model/ArticlesModel.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,11 @@ protected function getListQuery()
425425
$search = '%' . substr($search, 8) . '%';
426426
$query->where('(' . $db->quoteName('a.introtext') . ' LIKE :search1 OR ' . $db->quoteName('a.fulltext') . ' LIKE :search2)')
427427
->bind([':search1', ':search2'], $search);
428+
} elseif (stripos($search, 'checkedout:') === 0) {
429+
$search = '%' . substr($search, 11) . '%';
430+
$query->where('(' . $db->quoteName('uc.name') . ' LIKE :search1 OR ' . $db->quoteName('uc.username') . ' LIKE :search2)'
431+
. ' AND ' . $db->quoteName('a.checked_out') . ' IS NOT NULL')
432+
->bind([':search1', ':search2'], $search);
428433
} else {
429434
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
430435
$query->where(

administrator/components/com_guidedtours/config.xml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,28 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<config>
3+
<fieldset name="guidedtours_config" label="COM_GUIDEDTOURS">
4+
<field
5+
name="allowTourAutoStart"
6+
type="radio"
7+
label="COM_GUIDEDTOURS_CONFIG_USER_ALLOWTOURAUTOSTART_LABEL"
8+
description="COM_GUIDEDTOURS_CONFIG_USER_ALLOWTOURAUTOSTART_DESCRIPTION"
9+
layout="joomla.form.field.radio.switcher"
10+
default="1"
11+
>
12+
<option value="0">JNO</option>
13+
<option value="1">JYES</option>
14+
</field>
15+
16+
<field
17+
name="delayed_time"
18+
type="text"
19+
label="COM_GUIDEDTOURS_CONFIG_DELAYED_TIME_LABEL"
20+
description="COM_GUIDEDTOURS_CONFIG_DELAYED_TIME_DESCRIPTION"
21+
default="60"
22+
size="small"
23+
showon="allowTourAutoStart:1"
24+
/>
25+
</fieldset>
326
<fieldset
427
name="permissions"
528
label="JCONFIG_PERMISSIONS_LABEL"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
/**
4+
* @package Joomla.Administrator
5+
* @subpackage com_guidedtours
6+
*
7+
* @copyright (C) 2024 Open Source Matters, Inc. <https://www.joomla.org>
8+
* @license GNU General Public License version 2 or later; see LICENSE.txt
9+
*/
10+
11+
namespace Joomla\Component\Guidedtours\Administrator\Controller;
12+
13+
use Joomla\CMS\Event\AbstractEvent;
14+
use Joomla\CMS\Language\Text;
15+
use Joomla\CMS\MVC\Controller\BaseController;
16+
use Joomla\CMS\Plugin\PluginHelper;
17+
use Joomla\CMS\Response\JsonResponse;
18+
19+
// phpcs:disable PSR1.Files.SideEffects
20+
\defined('_JEXEC') or die;
21+
// phpcs:enable PSR1.Files.SideEffects
22+
23+
/**
24+
* The guided tours controller for ajax requests.
25+
*
26+
* @since __DEPLOY_VERSION__
27+
*/
28+
class AjaxController extends BaseController
29+
{
30+
/**
31+
* Ajax call used when cancelling, skipping or completing a tour.
32+
* It allows:
33+
* - the trigering of before and after events the user state is recorded
34+
* - the recording of the user behavior in the action logs
35+
*/
36+
public function fetchUserState()
37+
{
38+
$user = $this->app->getIdentity();
39+
40+
$tourId = $this->app->input->getInt('tid', 0);
41+
$stepNumber = $this->app->input->getString('sid', '');
42+
$context = $this->app->input->getString('context', '');
43+
44+
if ($user != null && $user->id > 0) {
45+
$actionState = '';
46+
47+
switch ($context) {
48+
case 'tour.complete':
49+
$actionState = 'completed';
50+
break;
51+
case 'tour.cancel':
52+
$actionState = 'delayed';
53+
break;
54+
case 'tour.skip':
55+
$actionState = 'skipped';
56+
break;
57+
}
58+
59+
PluginHelper::importPlugin('guidedtours');
60+
61+
// event onBeforeTourSaveUserState before save user tour state
62+
$beforeEvent = AbstractEvent::create(
63+
'onBeforeTourSaveUserState',
64+
[
65+
'subject' => new \stdClass(),
66+
'tourId' => $tourId,
67+
'actionState' => $actionState,
68+
'stepNumber' => $stepNumber,
69+
]
70+
);
71+
72+
$this->app->getDispatcher()->dispatch('onBeforeTourSaveUserState', $beforeEvent);
73+
74+
// Save the tour state only when the tour auto-starts.
75+
$tourModel = $this->getModel('Tour', 'Administrator');
76+
if ($tourModel->isAutostart($tourId)) {
77+
$result = $tourModel->saveTourUserState($tourId, $actionState);
78+
if ($result) {
79+
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATESAVED', $user->id, $tourId);
80+
} else {
81+
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED', $user->id, $tourId);
82+
}
83+
} else {
84+
$result = false;
85+
$message = Text::sprintf('COM_GUIDEDTOURS_USERSTATE_STATENOTSAVED', $user->id, $tourId);
86+
}
87+
88+
// event onAfterTourSaveUserState after save user tour state (may override message)
89+
$afterEvent = AbstractEvent::create(
90+
'onAfterTourSaveUserState',
91+
[
92+
'subject' => new \stdClass(),
93+
'tourId' => $tourId,
94+
'actionState' => $actionState,
95+
'stepNumber' => $stepNumber,
96+
'result' => $result,
97+
'message' => &$message,
98+
]
99+
);
100+
101+
$this->app->getDispatcher()->dispatch('onAfterTourSaveUserState', $afterEvent);
102+
103+
// Construct the response data
104+
$data = [
105+
'tourId' => $tourId,
106+
'stepId' => $stepNumber,
107+
'context' => $context,
108+
'state' => $actionState,
109+
];
110+
echo new JsonResponse($data, $message);
111+
$this->app->close();
112+
} else {
113+
// Construct the response data
114+
$data = [
115+
'success' => false,
116+
'tourId' => $tourId,
117+
];
118+
119+
$message = Text::_('COM_GUIDEDTOURS_USERSTATE_CONNECTEDONLY');
120+
echo new JsonResponse($data, $message, true);
121+
$this->app->close();
122+
}
123+
}
124+
}

administrator/components/com_guidedtours/src/Model/TourModel.php

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Joomla\Component\Guidedtours\Administrator\Model;
1212

13+
use Joomla\CMS\Date\Date;
1314
use Joomla\CMS\Factory;
1415
use Joomla\CMS\Language\Text;
1516
use Joomla\CMS\Log\Log;
@@ -530,20 +531,28 @@ public function setAutostart($id, $autostart)
530531
/**
531532
* Retrieve a tour's autostart value
532533
*
533-
* @param string $uid the uid of a tour
534+
* @param string $pk the id or uid of a tour
535+
*
536+
* @return boolean
534537
*
535538
* @since 5.1.0
536539
*/
537-
public function isAutostart($uid)
540+
public function isAutostart($pk): bool
538541
{
539542
$db = $this->getDatabase();
540543

541544
$query = $db->getQuery(true)
542545
->select($db->quoteName('autostart'))
543546
->from($db->quoteName('#__guidedtours'))
544-
->where($db->quoteName('published') . ' = 1')
545-
->where($db->quoteName('uid') . ' = :uid')
546-
->bind(':uid', $uid, ParameterType::STRING);
547+
->where($db->quoteName('published') . ' = 1');
548+
549+
if (\is_integer($pk)) {
550+
$query->where($db->quoteName('id') . ' = :id')
551+
->bind(':id', $pk, ParameterType::INTEGER);
552+
} else {
553+
$query->where($db->quoteName('uid') . ' = :uid')
554+
->bind(':uid', $pk, ParameterType::STRING);
555+
}
547556

548557
$db->setQuery($query);
549558

@@ -558,4 +567,64 @@ public function isAutostart($uid)
558567

559568
return $result;
560569
}
570+
571+
/**
572+
* Save a tour state for a specific user.
573+
*
574+
* @param int $id The id of the tour
575+
* @param string $state The label of the state to be saved (completed, delayed or skipped)
576+
*
577+
* @return boolean
578+
*
579+
* @since __DEPLOY_VERSION__
580+
*/
581+
public function saveTourUserState($id, $state = ''): bool
582+
{
583+
$user = $this->getCurrentUser();
584+
$db = $this->getDatabase();
585+
586+
$profileKey = 'guidedtour.id.' . $id;
587+
588+
// Check if the profile key already exists.
589+
$query = $db->getQuery(true)
590+
->select($db->quoteName('profile_value'))
591+
->from($db->quoteName('#__user_profiles'))
592+
->where($db->quoteName('user_id') . ' = :user_id')
593+
->where($db->quoteName('profile_key') . ' = :profileKey')
594+
->bind(':user_id', $user->id, ParameterType::INTEGER)
595+
->bind(':profileKey', $profileKey, ParameterType::STRING);
596+
597+
try {
598+
$result = $db->setQuery($query)->loadResult();
599+
} catch (\Exception $e) {
600+
return false;
601+
}
602+
603+
$tourState = [];
604+
605+
$tourState['state'] = $state;
606+
if ($state === 'delayed') {
607+
$tourState['time'] = Date::getInstance();
608+
}
609+
610+
$profileObject = (object)[
611+
'user_id' => $user->id,
612+
'profile_key' => $profileKey,
613+
'profile_value' => json_encode($tourState),
614+
'ordering' => 0,
615+
];
616+
617+
if (!\is_null($result)) {
618+
$values = json_decode($result, true);
619+
620+
// The profile is updated only when delayed. 'Completed' and 'Skipped' are final
621+
if (!empty($values) && $values['state'] === 'delayed') {
622+
$db->updateObject('#__user_profiles', $profileObject, ['user_id', 'profile_key']);
623+
}
624+
} else {
625+
$db->insertObject('#__user_profiles', $profileObject);
626+
}
627+
628+
return true;
629+
}
561630
}

administrator/components/com_media/src/Plugin/MediaActionPlugin.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
namespace Joomla\Component\Media\Administrator\Plugin;
1212

13+
use Joomla\CMS\Event\Model\PrepareFormEvent;
1314
use Joomla\CMS\Form\Form;
1415
use Joomla\CMS\HTML\HTMLHelper;
1516
use Joomla\CMS\Plugin\CMSPlugin;
@@ -34,6 +35,35 @@ class MediaActionPlugin extends CMSPlugin
3435
*/
3536
protected $autoloadLanguage = true;
3637

38+
/**
39+
* Returns an array of events this subscriber will listen to.
40+
*
41+
* @return array
42+
*
43+
* @since __DEPLOY_VERSION__
44+
*/
45+
public static function getSubscribedEvents(): array
46+
{
47+
return [
48+
'onContentPrepareForm' => 'onContentPrepareFormListener',
49+
];
50+
}
51+
52+
/**
53+
* The form event. Load additional parameters when available into the field form.
54+
* Only when the type of the form is of interest.
55+
*
56+
* @param PrepareFormEvent $event Event instance.
57+
*
58+
* @return void
59+
*
60+
* @since __DEPLOY_VERSION__
61+
*/
62+
public function onContentPrepareFormListener(PrepareFormEvent $event): void
63+
{
64+
$this->onContentPrepareForm($event->getForm(), $event->getData());
65+
}
66+
3767
/**
3868
* The form event. Load additional parameters when available into the field form.
3969
* Only when the type of the form is of interest.

0 commit comments

Comments
 (0)