first commit
This commit is contained in:
57
administrator/modules/mod_guidedtours/mod_guidedtours.xml
Normal file
57
administrator/modules/mod_guidedtours/mod_guidedtours.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<extension type="module" client="administrator" method="upgrade">
|
||||
<name>mod_guidedtours</name>
|
||||
<author>Joomla! Project</author>
|
||||
<creationDate>2023-02</creationDate>
|
||||
<copyright>(C) 2023 Open Source Matters, Inc.</copyright>
|
||||
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
|
||||
<authorEmail>admin@joomla.org</authorEmail>
|
||||
<authorUrl>www.joomla.org</authorUrl>
|
||||
<version>4.3.0</version>
|
||||
<description>MOD_GUIDEDTOURS_XML_DESCRIPTION</description>
|
||||
<namespace path="src">Joomla\Module\GuidedTours</namespace>
|
||||
<files>
|
||||
<folder module="mod_guidedtours">services</folder>
|
||||
<folder>src</folder>
|
||||
<folder>tmpl</folder>
|
||||
</files>
|
||||
<languages>
|
||||
<language tag="en-GB">language/en-GB/mod_guidedtours.ini</language>
|
||||
<language tag="en-GB">language/en-GB/mod_guidedtours.sys.ini</language>
|
||||
</languages>
|
||||
<help key="Admin_Modules:_Tours_Menu" />
|
||||
<config>
|
||||
<fields name="params">
|
||||
<fieldset name="basic">
|
||||
<field
|
||||
name="tourscount"
|
||||
type="number"
|
||||
label="MOD_GUIDEDTOURS_FIELD_TOUR_COUNT_LABEL"
|
||||
description="MOD_GUIDEDTOURS_FIELD_TOUR_COUNT_DESC"
|
||||
default="7"
|
||||
filter="integer"
|
||||
min="0"
|
||||
validate="number"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
<fieldset name="advanced">
|
||||
<field
|
||||
name="layout"
|
||||
type="modulelayout"
|
||||
label="JFIELD_ALT_LAYOUT_LABEL"
|
||||
class="form-select"
|
||||
validate="moduleLayout"
|
||||
/>
|
||||
|
||||
<field
|
||||
name="moduleclass_sfx"
|
||||
type="textarea"
|
||||
label="COM_MODULES_FIELD_MODULECLASS_SFX_LABEL"
|
||||
rows="3"
|
||||
validate="CssIdentifier"
|
||||
/>
|
||||
</fieldset>
|
||||
</fields>
|
||||
</config>
|
||||
</extension>
|
||||
41
administrator/modules/mod_guidedtours/services/provider.php
Normal file
41
administrator/modules/mod_guidedtours/services/provider.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage mod_guidedtours
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\Extension\Service\Provider\HelperFactory;
|
||||
use Joomla\CMS\Extension\Service\Provider\Module;
|
||||
use Joomla\CMS\Extension\Service\Provider\ModuleDispatcherFactory;
|
||||
use Joomla\DI\Container;
|
||||
use Joomla\DI\ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* The guided tours module service provider.
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
return new class () implements ServiceProviderInterface {
|
||||
/**
|
||||
* Registers the service provider with a DI container.
|
||||
*
|
||||
* @param Container $container The DI container.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container->registerServiceProvider(new ModuleDispatcherFactory('\\Joomla\\Module\\GuidedTours'));
|
||||
$container->registerServiceProvider(new HelperFactory('\\Joomla\\Module\\GuidedTours\\Administrator\\Helper'));
|
||||
|
||||
$container->registerServiceProvider(new Module());
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage mod_guidedtours
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Module\GuidedTours\Administrator\Dispatcher;
|
||||
|
||||
use Joomla\CMS\Dispatcher\AbstractModuleDispatcher;
|
||||
use Joomla\CMS\Helper\HelperFactoryAwareInterface;
|
||||
use Joomla\CMS\Helper\HelperFactoryAwareTrait;
|
||||
use Joomla\CMS\Plugin\PluginHelper;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Dispatcher class for mod_guidedtours
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
class Dispatcher extends AbstractModuleDispatcher implements HelperFactoryAwareInterface
|
||||
{
|
||||
use HelperFactoryAwareTrait;
|
||||
|
||||
/**
|
||||
* Runs the dispatcher.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function dispatch()
|
||||
{
|
||||
// The guided tour will not show if no user is logged in.
|
||||
$user = $this->getApplication()->getIdentity();
|
||||
if ($user === null || $user->id === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The module can't show if the plugin is not enabled.
|
||||
if (!PluginHelper::isEnabled('system', 'guidedtours')) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout data.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
protected function getLayoutData()
|
||||
{
|
||||
$data = parent::getLayoutData();
|
||||
|
||||
$data['tours'] = $this->getHelperFactory()->getHelper('GuidedToursHelper')->getTours($data['params'], $this->getApplication());
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage mod_guidedtours
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
namespace Joomla\Module\GuidedTours\Administrator\Helper;
|
||||
|
||||
use Joomla\CMS\Application\AdministratorApplication;
|
||||
use Joomla\CMS\Language\Multilanguage;
|
||||
use Joomla\Registry\Registry;
|
||||
use Joomla\Uri\Uri;
|
||||
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
\defined('_JEXEC') or die;
|
||||
// phpcs:enable PSR1.Files.SideEffects
|
||||
|
||||
/**
|
||||
* Helper for mod_guidedtours
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
class GuidedToursHelper
|
||||
{
|
||||
/**
|
||||
* Get a list of tours from a specific context.
|
||||
*
|
||||
* @param Registry $params Object holding the module parameters
|
||||
* @param AdministratorApplication $app The application
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public function getTours(Registry $params, AdministratorApplication $app)
|
||||
{
|
||||
$factory = $app->bootComponent('com_guidedtours')->getMVCFactory();
|
||||
|
||||
$user = $app->getIdentity();
|
||||
|
||||
// Get an instance of the guided tour model
|
||||
$tourModel = $factory->createModel('Tours', 'Administrator', ['ignore_request' => true]);
|
||||
|
||||
$tourModel->setState('filter.published', 1);
|
||||
$tourModel->setState('filter.access', $app->getIdentity()->getAuthorisedViewLevels());
|
||||
|
||||
if (Multilanguage::isEnabled()) {
|
||||
$tourModel->setState('filter.language', ['*', $app->getLanguage()->getTag()]);
|
||||
}
|
||||
|
||||
$items = $tourModel->getItems();
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
// The user can only see the tours of extensions that are allowed.
|
||||
$uri = new Uri($item->url);
|
||||
|
||||
if ($extension = $uri->getVar('option')) {
|
||||
if ($extension === 'com_categories') {
|
||||
$extension = $uri->getVar('extension');
|
||||
}
|
||||
if (!$user->authorise('core.manage', $extension)) {
|
||||
unset($items[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
142
administrator/modules/mod_guidedtours/tmpl/default.php
Normal file
142
administrator/modules/mod_guidedtours/tmpl/default.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Joomla.Administrator
|
||||
* @subpackage mod_guidedtours
|
||||
*
|
||||
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Uri\Uri;
|
||||
|
||||
$hideLinks = $app->getInput()->getBool('hidemainmenu');
|
||||
|
||||
if ($hideLinks || !$tours) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the Bootstrap Dropdown
|
||||
$app->getDocument()
|
||||
->getWebAssetManager()
|
||||
->useScript('bootstrap.dropdown');
|
||||
|
||||
$lang = $app->getLanguage();
|
||||
$extension = $app->getInput()->get('option');
|
||||
$listTours = [];
|
||||
$allTours = [];
|
||||
$toursCount = $params->get('tourscount', 7);
|
||||
|
||||
foreach ($tours as $tour) :
|
||||
if ($toursCount > 0 && count(array_intersect(['*', $extension], $tour->extensions))) :
|
||||
$listTours[] = $tour;
|
||||
$toursCount--;
|
||||
endif;
|
||||
|
||||
$uri = new Uri($tour->url);
|
||||
|
||||
// We assume the url is the starting point
|
||||
$key = $uri->getVar('option') ?? Text::_('MOD_GUIDEDTOURS_GENERIC_TOUR');
|
||||
|
||||
if (!isset($allTours[$key])) :
|
||||
$lang->load("$key.sys", JPATH_ADMINISTRATOR)
|
||||
|| $lang->load("$key.sys", JPATH_ADMINISTRATOR . '/components/' . $key);
|
||||
|
||||
$allTours[$key] = [];
|
||||
endif;
|
||||
|
||||
$allTours[$key][] = $tour;
|
||||
endforeach;
|
||||
|
||||
?>
|
||||
<div class="header-item-content dropdown header-tours d-none d-sm-block">
|
||||
<button class="dropdown-toggle d-flex align-items-center ps-0 py-0" data-bs-toggle="dropdown" type="button" title="<?php echo Text::_('MOD_GUIDEDTOURS_MENU'); ?>">
|
||||
<div class="header-item-icon">
|
||||
<span class="icon-map-signs" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="header-item-text">
|
||||
<?php echo Text::_('MOD_GUIDEDTOURS_MENU'); ?>
|
||||
</div>
|
||||
<span class="icon-angle-down" aria-hidden="true"></span>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-end">
|
||||
<?php foreach ($listTours as $tour) : ?>
|
||||
<button type="button" class="button-start-guidedtour dropdown-item" data-id="<?php echo $tour->id ?>">
|
||||
<span class="icon-map-signs" aria-hidden="true"></span>
|
||||
<?php echo $tour->title; ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
<button type="button" class="dropdown-item text-center" data-bs-toggle="modal" data-bs-target="#modGuidedTours-modal">
|
||||
<?php echo Text::_('MOD_GUIDEDTOURS_SHOW_ALL'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
$modalParams = [
|
||||
'title' => Text::_('MOD_GUIDEDTOURS_START_TOUR'),
|
||||
'footer' => '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">'
|
||||
. Text::_('JLIB_HTML_BEHAVIOR_CLOSE') . '</button>',
|
||||
];
|
||||
|
||||
$modalHtml = [];
|
||||
$modalHtml[] = '<div class="p-3">';
|
||||
$modalHtml[] = '<div class="row">';
|
||||
foreach ($allTours as $extension => $tours) :
|
||||
$modalHtml[] = '<div class="col-lg-6">';
|
||||
$modalHtml[] = '<h4>' . Text::_($extension) . '</h4>';
|
||||
$modalHtml[] = '<ul class="list-unstyled">';
|
||||
foreach ($tours as $tour) :
|
||||
$modalHtml[] = '<li>';
|
||||
$modalHtml[] = '<a href="#" role="button" class="button-start-guidedtour" data-id="' . (int) $tour->id . '">' . htmlentities($tour->title) . '</a>';
|
||||
$modalHtml[] = '</li>';
|
||||
endforeach;
|
||||
$modalHtml[] = '</ul>';
|
||||
$modalHtml[] = '</div>';
|
||||
endforeach;
|
||||
$modalHtml[] = '</div>';
|
||||
$modalHtml[] = '</div>';
|
||||
|
||||
$modalBody = implode($modalHtml);
|
||||
|
||||
$modalCode = HTMLHelper::_('bootstrap.renderModal', 'modGuidedTours-modal', $modalParams, $modalBody);
|
||||
|
||||
// We have to attach the modal to the body, otherwise we have problems with the backdrop
|
||||
$app->getDocument()->getWebAssetManager()->addInlineScript("
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.body.insertAdjacentHTML('beforeend', " . json_encode($modalCode) . ");
|
||||
const modal = document.getElementById('modGuidedTours-modal');
|
||||
|
||||
// add all the elements inside modal which you want to make focusable
|
||||
const focusableElements = 'button, [href]';
|
||||
const firstFocusableElement = modal.querySelectorAll(focusableElements)[0]; // get first element to be focused inside modal
|
||||
const focusableContent = modal.querySelectorAll(focusableElements);
|
||||
const lastFocusableElement = focusableContent[focusableContent.length - 1]; // get last element to be focused inside modal
|
||||
|
||||
document.addEventListener('keydown', function(e) {
|
||||
let isTabPressed = e.key === 'Tab' || e.keyCode === 9;
|
||||
|
||||
if (!isTabPressed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.shiftKey) { // if shift key pressed for shift + tab combination
|
||||
if (document.activeElement === firstFocusableElement) {
|
||||
lastFocusableElement.focus(); // add focus for the last focusable element
|
||||
e.preventDefault();
|
||||
}
|
||||
} else { // if tab key is pressed
|
||||
if (document.activeElement === lastFocusableElement) { // if focused has reached to last focusable element then focus first focusable element after pressing tab
|
||||
firstFocusableElement.focus(); // add focus for the first focusable element
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
firstFocusableElement.focus();
|
||||
});
|
||||
");
|
||||
Reference in New Issue
Block a user