first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<access component="com_guidedtours">
<section name="component">
<action name="core.admin" title="JACTION_ADMIN" />
<action name="core.options" title="JACTION_OPTIONS" />
<action name="core.manage" title="JACTION_MANAGE" />
<action name="core.create" title="JACTION_CREATE" />
<action name="core.delete" title="JACTION_DELETE" />
<action name="core.edit" title="JACTION_EDIT" />
<action name="core.edit.state" title="JACTION_EDITSTATE" />
<action name="core.edit.own" title="JACTION_EDITOWN" />
</section>
</access>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<config>
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
validate="rules"
filter="rules"
component="com_guidedtours"
section="component"
/>
</fieldset>
</config>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fields name="filter">
<field
name="search"
type="text"
label="COM_GUIDEDTOURS_STEP_FILTER_SEARCH_LABEL"
description="COM_GUIDEDTOURS_STEP_FILTER_SEARCH_DESC"
inputmode="search"
hint="JSEARCH_FILTER"
/>
<field
name="published"
type="status"
label="JOPTION_SELECT_PUBLISHED"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_PUBLISHED</option>
</field>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
default="a.ordering ASC"
validate="options"
>
<option value="">JGLOBAL_SORT_BY</option>
<option value="a.ordering ASC">JGRID_HEADING_ORDERING_ASC</option>
<option value="a.ordering DESC">JGRID_HEADING_ORDERING_DESC</option>
<option value="a.published ASC">JSTATUS_ASC</option>
<option value="a.published DESC">JSTATUS_DESC</option>
<option value="a.type ASC">COM_GUIDEDTOURS_ORDER_TYPE_ASC</option>
<option value="a.type DESC">COM_GUIDEDTOURS_ORDER_TYPE_DESC</option>
<option value="a.id ASC">JGRID_HEADING_ID_ASC</option>
<option value="a.id DESC">JGRID_HEADING_ID_DESC</option>
</field>
<field
name="limit"
type="limitbox"
label="JGLOBAL_LIST_LIMIT"
default="25"
class="js-select-submit-on-change"
/>
</fields>
</form>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fields name="filter">
<field
name="search"
type="text"
label="COM_GUIDEDTOURS_FILTER_SEARCH_LABEL"
description="COM_GUIDEDTOURS_FILTER_SEARCH_DESC"
inputmode="search"
hint="JSEARCH_FILTER"
/>
<field
name="published"
type="status"
label="JOPTION_SELECT_PUBLISHED"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_PUBLISHED</option>
</field>
<field
name="access"
type="accesslevel"
label="JGRID_HEADING_ACCESS"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_ACCESS</option>
</field>
<field
name="language"
type="contentlanguage"
label="JGRID_HEADING_LANGUAGE"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_LANGUAGE</option>
<option value="*">JALL</option>
</field>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
default="a.ordering ASC"
validate="options"
>
<option value="">JGLOBAL_SORT_BY</option>
<option value="a.ordering ASC">JGRID_HEADING_ORDERING_ASC</option>
<option value="a.ordering DESC">JGRID_HEADING_ORDERING_DESC</option>
<option value="a.published ASC">JSTATUS_ASC</option>
<option value="a.published DESC">JSTATUS_DESC</option>
<option value="a.access ASC">JGRID_HEADING_ACCESS_ASC</option>
<option value="a.access DESC">JGRID_HEADING_ACCESS_DESC</option>
<option value="a.language ASC" requires="multilanguage">JGRID_HEADING_LANGUAGE_ASC</option>
<option value="a.language DESC" requires="multilanguage">JGRID_HEADING_LANGUAGE_DESC</option>
<option value="a.id ASC">JGRID_HEADING_ID_ASC</option>
<option value="a.id DESC">JGRID_HEADING_ID_DESC</option>
</field>
<field
name="limit"
type="limitbox"
label="JGLOBAL_LIST_LIMIT"
default="25"
class="js-select-submit-on-change"
/>
</fields>
</form>

View File

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<form addfieldprefix="Joomla\Component\Guidedtours\Administrator\Field">
<config>
<inlinehelp button="show"/>
</config>
<field
name="id"
type="text"
label="JGLOBAL_FIELD_ID_LABEL"
default="0"
class="readonly"
readonly="true"
/>
<field
name="position"
type="list"
label="COM_GUIDEDTOURS_STEP_POSITION_LABEL"
description="COM_GUIDEDTOURS_STEP_POSITION_DESC"
default="center"
validate="options"
>
<option value="bottom">JGLOBAL_BOTTOM</option>
<option value="center">JGLOBAL_CENTER</option>
<option value="left">JGLOBAL_LEFT</option>
<option value="right">JGLOBAL_RIGHT</option>
<option value="top">JGLOBAL_TOP</option>
</field>
<field
name="target"
type="text"
label="COM_GUIDEDTOURS_STEP_TARGET_LABEL"
description="COM_GUIDEDTOURS_STEP_TARGET_DESC"
/>
<field
name="tour_id"
type="hidden"
class="readonly"
readonly="true"
/>
<field
name="title"
type="text"
label="COM_GUIDEDTOURS_STEP_TITLE"
required="true"
/>
<field
name="title_translation"
type="text"
label="JGLOBAL_TITLE"
readonly="true"
/>
<field
name="description"
type="editor"
label="JGLOBAL_DESCRIPTION"
filter="\Joomla\CMS\Component\ComponentHelper::filterText"
buttons="true"
hide="readmore,pagebreak,module,menu,fields,contact,article"
/>
<field
name="description_translation"
type="editor"
label="JGLOBAL_DESCRIPTION"
filter="\Joomla\CMS\Component\ComponentHelper::filterText"
buttons="false"
readonly="true"
/>
<field
name="published"
type="list"
label="JSTATUS"
default="1"
class="form-select-color-state"
validate="options"
>
<option value="1">JPUBLISHED</option>
<option value="0">JUNPUBLISHED</option>
<option value="2">JARCHIVED</option>
<option value="-2">JTRASHED</option>
</field>
<field
name="type"
type="list"
label="COM_GUIDEDTOURS_STEP_TYPE_LABEL"
description="COM_GUIDEDTOURS_STEP_TYPE_DESC"
default="0"
validate="options"
>
<option value="0">COM_GUIDEDTOURS_FIELD_VALUE_STEP_TYPE_NEXT</option>
<option value="1">COM_GUIDEDTOURS_FIELD_VALUE_STEP_TYPE_REDIRECT</option>
<option value="2">COM_GUIDEDTOURS_FIELD_VALUE_STEP_TYPE_INTERACTIVE</option>
</field>
<field
name="url"
type="url"
label="COM_GUIDEDTOURS_TYPE_REDIRECT_URL_LABEL"
description="COM_GUIDEDTOURS_TYPE_REDIRECT_URL_DESC"
showon="type:1"
filter="string"
/>
<field
name="interactive_type"
type="list"
label="COM_GUIDEDTOURS_TYPE_INTERACTIVE_STEP_LABEL"
description="COM_GUIDEDTOURS_TYPE_INTERACTIVE_STEP_DESC"
showon="type:2"
filter="string"
validate="options"
>
<option value="1">COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_FORM_SUBMIT</option>
<option value="2">COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_TEXT_FIELD</option>
<option value="4">COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_BUTTON</option>
<option value="3">COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_OTHER</option>
</field>
<field
name="checked_out"
type="hidden"
filter="unset"
/>
<field
name="checked_out_time"
type="hidden"
filter="unset"
/>
<field
name="created_by"
type="user"
label="JGLOBAL_FIELD_CREATED_BY_LABEL"
validate="UserId"
/>
<field
name="created"
type="calendar"
label="JGLOBAL_CREATED_DATE"
translateformat="true"
showtime="true"
filter="user_utc"
/>
<field
name="modified_by"
type="user"
label="JGLOBAL_FIELD_MODIFIED_BY_LABEL"
class="readonly"
readonly="true"
filter="unset"
validate="UserId"
/>
<field
name="modified"
type="calendar"
label="JGLOBAL_FIELD_MODIFIED_LABEL"
class="readonly"
readonly="true"
translateformat="true"
showtime="true"
filter="user_utc"
/>
<field
name="language"
type="contentlanguage"
label="JFIELD_LANGUAGE_LABEL"
default="*"
>
<option value="*">JALL</option>
</field>
<field
name="note"
type="text"
label="COM_GUIDEDTOURS_FIELD_NOTE_LABEL"
maxlength="255"
/>
</form>

View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<form addfieldprefix="Joomla\Component\Guidedtours\Administrator\Field">
<config>
<inlinehelp button="show"/>
</config>
<field
name="id"
type="text"
label="JGLOBAL_FIELD_ID_LABEL"
class="readonly"
readonly="true"
default="0"
/>
<field
name="title"
type="text"
label="COM_GUIDEDTOURS_TITLE"
required="true"
/>
<field
name="title_translation"
type="text"
label="JGLOBAL_TITLE"
readonly="true"
/>
<field
name="uid"
type="text"
label="COM_GUIDEDTOURS_TOUR_UID_LABEL"
description="COM_GUIDEDTOURS_TOUR_UID_DESC"
hint="COM_GUIDEDTOURS_TOUR_UID_HINT"
required="false"
/>
<field
name="description"
type="editor"
label="JGLOBAL_DESCRIPTION"
filter="\Joomla\CMS\Component\ComponentHelper::filterText"
buttons="true"
hide="readmore,pagebreak,module,menu,fields,contact,article"
/>
<field
name="description_translation"
type="editor"
label="JGLOBAL_DESCRIPTION"
filter="\Joomla\CMS\Component\ComponentHelper::filterText"
buttons="false"
readonly="true"
/>
<field
name="published"
type="list"
label="JSTATUS"
default="1"
class="form-select-color-state"
validate="options"
>
<option value="1">JPUBLISHED</option>
<option value="0">JUNPUBLISHED</option>
<option value="2">JARCHIVED</option>
<option value="-2">JTRASHED</option>
</field>
<field
name="checked_out"
type="hidden"
filter="unset"
/>
<field
name="checked_out_time"
type="hidden"
filter="unset"
/>
<field
name="access"
type="accesslevel"
label="JFIELD_ACCESS_LABEL"
filter="UINT"
validate="options"
/>
<field
name="created_by"
type="user"
label="JGLOBAL_FIELD_CREATED_BY_LABEL"
validate="UserId"
/>
<field
name="created"
type="calendar"
label="JGLOBAL_CREATED_DATE"
translateformat="true"
showtime="true"
filter="user_utc"
/>
<field
name="modified_by"
type="user"
label="JGLOBAL_FIELD_MODIFIED_BY_LABEL"
class="readonly"
readonly="true"
filter="unset"
validate="UserId"
/>
<field
name="modified"
type="calendar"
label="JGLOBAL_FIELD_MODIFIED_LABEL"
class="readonly"
readonly="true"
translateformat="true"
showtime="true"
filter="user_utc"
/>
<field
name="language"
type="contentlanguage"
label="JFIELD_LANGUAGE_LABEL"
default="*"
>
<option value="*">JALL</option>
</field>
<field
name="note"
type="text"
label="COM_GUIDEDTOURS_FIELD_NOTE_LABEL"
maxlength="255"
/>
<field
name="url"
type="url"
label="COM_GUIDEDTOURS_URL_LABEL"
description="COM_GUIDEDTOURS_URL_DESC"
required="true"
/>
<field
name="extensions"
type="components"
label="COM_GUIDEDTOURS_EXTENSIONS_LABEL"
description="COM_GUIDEDTOURS_EXTENSIONS_DESC"
required="true"
multiple="multiple"
layout="joomla.form.field.list-fancy-select"
default="*"
>
<option value="*">JALL</option>
</field>
</form>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="component" method="upgrade">
<name>com_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>COM_GUIDEDTOURS_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Component\Guidedtours</namespace>
<administration>
<filename>access.xml</filename>
<filename>config.xml</filename>
<filename>guidedtours.xml</filename>
<folder>forms</folder>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
<languages folder="administrator">
<language tag="en-GB">language/en-GB/com_guidedtours.ini</language>
<language tag="en-GB">language/en-GB/com_guidedtours.sys.ini</language>
</languages>
</administration>
</extension>

View File

@ -0,0 +1,59 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Dispatcher\ComponentDispatcherFactoryInterface;
use Joomla\CMS\Extension\ComponentInterface;
use Joomla\CMS\Extension\Service\Provider\ComponentDispatcherFactory;
use Joomla\CMS\Extension\Service\Provider\MVCFactory;
use Joomla\CMS\HTML\Registry;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\Component\Guidedtours\Administrator\Extension\GuidedtoursComponent;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* The Guidedtours 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 MVCFactory('\\Joomla\\Component\\Guidedtours'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\Guidedtours'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new GuidedtoursComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setRegistry($container->get(Registry::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
return $component;
}
);
}
};

View File

@ -0,0 +1,75 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component Controller
*
* @since 4.3.0
*/
class DisplayController extends BaseController
{
/**
* The default view.
*
* @var string
* @since 4.3.0
*/
protected $default_view = 'tours';
/**
* Method to display a view.
*
* @param boolean $cachable If true, the view output will be cached
* @param array $urlparams An array of safe URL parameters and their variable types.
* @see \Joomla\CMS\Filter\InputFilter::clean() for valid values.
*
* @return static |boolean This object to support chaining. False on failure.
*
* @since 4.3.0
*/
public function display($cachable = false, $urlparams = [])
{
$view = $this->input->get('view', $this->default_view);
$layout = $this->input->get('layout', 'default');
$id = $this->input->getInt('id');
// Show messages about the disabled plugin
if ($view === 'tours' && !PluginHelper::isEnabled('system', 'guidedtours')) {
$this->app->enqueueMessage(Text::_('COM_GUIDEDTOURS_PLUGIN_DISABLED'), 'error');
}
if ($view === 'tour' && $layout === 'edit' && !$this->checkEditId('com_guidedtours.edit.tour', $id)) {
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id), 'error');
$this->setRedirect(Route::_('index.php?option=com_guidedtours&view=tours', false));
return false;
}
if ($view === 'step' && $layout === 'edit' && !$this->checkEditId('com_guidedtours.edit.step', $id)) {
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id), 'error');
$this->setRedirect(Route::_('index.php?option=com_guidedtours&view=steps', false));
return false;
}
return parent::display();
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Controller;
use Joomla\CMS\MVC\Controller\FormController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Controller for a single step
*
* @since 4.3.0
*/
class StepController extends FormController
{
}

View File

@ -0,0 +1,42 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Controller;
use Joomla\CMS\MVC\Controller\AdminController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component Controller
*
* @since 4.3.0
*/
class StepsController extends AdminController
{
/**
* Proxy for getModel.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config The array of possible config values. Optional.
*
* @return \Joomla\CMS\MVC\Model\BaseDatabaseModel
*
* @since 4.3.0
*/
public function getModel($name = 'Step', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Controller;
use Joomla\CMS\MVC\Controller\FormController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Controller for a single Tour
*
* @since 4.3.0
*/
class TourController extends FormController
{
}

View File

@ -0,0 +1,62 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\Router\Route;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Guidedtours list controller class.
*
* @since 4.3.0
*/
class ToursController extends AdminController
{
/**
* Proxy for getModel.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config The array of possible config values. Optional.
*
* @return \Joomla\CMS\MVC\Model\BaseDatabaseModel
*
* @since 4.3.0
*/
public function getModel($name = 'Tour', $prefix = 'Administrator', $config = ['ignore_request' => true])
{
return parent::getModel($name, $prefix, $config);
}
public function duplicate()
{
$this->checkToken();
$pks = (array) $this->input->post->get('cid', [], 'int');
$pks = array_filter($pks);
try {
if (empty($pks)) {
throw new \Exception(Text::_('COM_GUIDEDTOURS_ERROR_NO_GUIDEDTOURS_SELECTED'));
}
$model = $this->getModel();
$model->duplicate($pks);
$this->setMessage(Text::plural('COM_GUIDEDTOURS_TOURS_DUPLICATED', \count($pks)));
} catch (\Exception $e) {
$this->app->enqueueMessage($e->getMessage(), 'warning');
}
$this->setRedirect(Route::_('index.php?option=com_guidedtours&view=tours' . $this->getRedirectToListAppend(), false));
}
}

View File

@ -0,0 +1,119 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Extension;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use Psr\Container\ContainerInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component class for com_guidedtours
*
* @since 4.3.0
*/
class GuidedtoursComponent extends MVCComponent implements BootableExtensionInterface
{
use HTMLRegistryAwareTrait;
/**
* The step type
*
* @since 4.3.0
*/
public const STEP_TYPE_NAMES = [
self::STEP_NEXT => 'COM_GUIDEDTOURS_FIELD_VALUE_STEP_TYPE_NEXT',
self::STEP_REDIRECT => 'COM_GUIDEDTOURS_FIELD_VALUE_STEP_TYPE_REDIRECT',
self::STEP_INTERACTIVE => 'COM_GUIDEDTOURS_FIELD_VALUE_STEP_TYPE_INTERACTIVE',
];
/**
* A regular step.
*
* @since 4.3.0
*/
public const STEP_NEXT = 0;
/**
* A step that redirects to another page.
*
* @since 4.3.0
*/
public const STEP_REDIRECT = 1;
/**
* A step that allows interactions from the user.
*
* @since 4.3.0
*/
public const STEP_INTERACTIVE = 2;
/**
* The step interactive type names
*
* @since 4.3.0
*/
public const STEP_INTERACTIVETYPE_NAMES = [
self::STEP_INTERACTIVETYPE_FORM_SUBMIT => 'COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_FORM_SUBMIT',
self::STEP_INTERACTIVETYPE_TEXT => 'COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_TEXT_FIELD',
self::STEP_INTERACTIVETYPE_BUTTON => 'COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_BUTTON',
self::STEP_INTERACTIVETYPE_OTHER => 'COM_GUIDEDTOURS_FIELD_VALUE_INTERACTIVESTEP_TYPE_OTHER',
];
/**
* An interactive step where a user clicks on a form button.
*
* @since 4.3.0
*/
public const STEP_INTERACTIVETYPE_FORM_SUBMIT = 1;
/**
* An interactive step where a user enters text.
*
* @since 4.3.0
*/
public const STEP_INTERACTIVETYPE_TEXT = 2;
/**
* An interactive step where a user clicks on a button.
*
* @since 4.3.0
*/
public const STEP_INTERACTIVETYPE_BUTTON = 4;
/**
* An interactive step for other fields.
*
* @since 4.3.0
*/
public const STEP_INTERACTIVETYPE_OTHER = 3;
/**
* Booting the extension. This is the function to set up the environment of the extension like
* registering new class loaders, etc.
*
* If required, some initial set up can be done from services of the container, eg.
* registering HTML services.
*
* @param ContainerInterface $container The container
*
* @return void
*
* @since 4.3.0
*/
public function boot(ContainerInterface $container)
{
}
}

View File

@ -0,0 +1,102 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Helper;
use Joomla\CMS\Factory;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Guidedtours component helper.
*
* @since 5.0.0
*/
class GuidedtoursHelper
{
/**
* Load the translation files for an Guided Tour
*
* @param string $uid Guided Tour Unique Identifier
* @param boolean $steps Should tour steps language file be loaded
*
* @return void
*
* @since 5.0.0
*/
public static function loadTranslationFiles($uid, bool $steps = false)
{
static $cache = [];
$uid = strtolower($uid);
if (isset($cache[$uid])) {
return;
}
$lang = Factory::getLanguage();
// The uid has an extension separator so we need to check the extension language files
if (strpos($uid, '.') > 0) {
list($extension, $tourid) = explode('.', $uid, 2);
$source = '';
switch (substr($extension, 0, 3)) {
case 'com':
$source = JPATH_ADMINISTRATOR . '/components/' . $extension;
break;
case 'lib':
$source = JPATH_LIBRARIES . '/' . substr($extension, 4);
break;
case 'mod':
$source = JPATH_SITE . '/modules/' . $extension;
break;
case 'plg':
$parts = explode('_', $extension, 3);
if (\count($parts) > 2) {
$source = JPATH_PLUGINS . '/' . $parts[1] . '/' . $parts[2];
}
break;
case 'pkg':
$source = JPATH_SITE;
break;
case 'tpl':
$source = JPATH_BASE . '/templates/' . substr($extension, 4);
break;
default:
$source = JPATH_ADMINISTRATOR . '/components/com_' . $extension;
break;
}
$lang->load($extension . '.' . str_replace('-', '_', $tourid), JPATH_ADMINISTRATOR)
|| $lang->load($extension . '.' . str_replace('-', '_', $tourid), $source);
if ($steps) {
$lang->load($extension . '.' . str_replace('-', '_', $tourid) . '_steps', JPATH_ADMINISTRATOR)
|| $lang->load($extension . '.' . str_replace('-', '_', $tourid) . '_steps', $source);
}
} else {
$lang->load('guidedtours.' . str_replace('-', '_', $uid), JPATH_ADMINISTRATOR);
if ($steps) {
$lang->load('guidedtours.' . str_replace('-', '_', $uid) . '_steps', JPATH_ADMINISTRATOR);
}
}
$cache[$uid] = true;
}
}

View File

@ -0,0 +1,271 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Table\Table;
use Joomla\Component\Guidedtours\Administrator\Helper\GuidedtoursHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Item Model for a single tour.
*
* @since 4.3.0
*/
class StepModel extends AdminModel
{
/**
* The prefix to use with controller messages.
*
* @var string
* @since 4.3.0
*/
protected $text_prefix = 'COM_GUIDEDTOURS';
/**
* Type alias for content type
*
* @var string
* @since 4.3.0
*/
public $typeAlias = 'com_guidedtours.step';
/**
* Method to test whether a record can be deleted.
*
* @param object $record A record object.
*
* @return boolean True if allowed to delete the record. Defaults to the permission for the component.
*
* @since 4.3.0
*/
protected function canDelete($record)
{
if (empty($record->id) || $record->published != -2) {
return false;
}
return parent::canDelete($record);
}
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*
* @since 4.3.0
*/
public function save($data)
{
$table = $this->getTable();
$input = Factory::getApplication()->getInput();
$tour = $this->getTable('Tour');
$tour->load($data['tour_id']);
// Language keys must include GUIDEDTOUR to prevent save issues
if (strpos($data['description'], 'GUIDEDTOUR') !== false) {
$data['description'] = strip_tags($data['description']);
}
// Make sure we use the correct extension when editing an existing tour
$key = $table->getKeyName();
$pk = $data[$key] ?? (int) $this->getState($this->getName() . '.id');
if ($pk > 0) {
$table->load($pk);
if ((int) $table->tour_id) {
$data['tour_id'] = (int) $table->tour_id;
}
}
if ($input->get('task') == 'save2copy') {
$origTable = clone $this->getTable();
$origTable->load($input->getInt('id'));
$data['published'] = 0;
}
return parent::save($data);
}
/**
* Prepare and sanitise the table prior to saving.
*
* @param \Joomla\CMS\Table\Table $table The Table object
*
* @return void
*
* @since 4.3.0
*/
protected function prepareTable($table)
{
$date = Factory::getDate()->toSql();
$table->title = htmlspecialchars_decode($table->title, ENT_QUOTES);
if (empty($table->id)) {
// Set the values
$table->created = $date;
// Set ordering to the last item if not set
if (empty($table->ordering)) {
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('MAX(ordering)')
->from($db->quoteName('#__guidedtour_steps'));
$db->setQuery($query);
$max = $db->loadResult();
$table->ordering = $max + 1;
}
} else {
// Set the values
$table->modified = $date;
$table->modified_by = $this->getCurrentUser()->id;
}
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $type The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return Table A Table object
*
* @since 4.3.0
* @throws \Exception
*/
public function getTable($type = 'Step', $prefix = 'Administrator', $config = [])
{
return parent::getTable($type, $prefix, $config);
}
/**
* Abstract method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return \JForm|boolean A JForm object on success, false on failure
*
* @since 4.3.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_guidedtours.step',
'step',
[
'control' => 'jform',
'load_data' => $loadData,
]
);
if (empty($form)) {
return false;
}
$id = $data['id'] ?? $form->getValue('id');
$item = $this->getItem($id);
// Modify the form based on access controls.
if (!$this->canEditState((object) $item)) {
$form->setFieldAttribute('published', 'disabled', 'true');
$form->setFieldAttribute('published', 'required', 'false');
$form->setFieldAttribute('published', 'filter', 'unset');
}
// Disables language field selection
$form->setFieldAttribute('language', 'readonly', 'true');
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 4.3.0
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState(
'com_guidedtours.edit.step.data',
[]
);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
/**
* Method to get a single record.
*
* @param integer $pk The id of the primary key.
*
* @return CMSObject|boolean Object on success, false on failure.
*
* @since 4.3.0
*/
public function getItem($pk = null)
{
if ($result = parent::getItem($pk)) {
$app = Factory::getApplication();
/** @var \Joomla\Component\Guidedtours\Administrator\Model\TourModel $tourModel */
$tourModel = $app->bootComponent('com_guidedtours')
->getMVCFactory()->createModel('Tour', 'Administrator', ['ignore_request' => true]);
if (!empty($result->id)) {
// Editing an existing step
$tour = $tourModel->getItem($result->tour_id);
GuidedtoursHelper::loadTranslationFiles($tour->uid, true);
$result->title_translation = Text::_($result->title);
$result->description_translation = Text::_($result->description);
} else {
// Creating a new step so we get the tour id from the session data
$tourId = $app->getUserState('com_guidedtours.tour_id');
$tour = $tourModel->getItem($tourId);
// Sets step language to parent tour language
$result->language = !empty($tour->language) ? $tour->language : '*';
// Set the step's tour id
$result->tour_id = $tourId;
}
}
return $result;
}
}

View File

@ -0,0 +1,255 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Component\Guidedtours\Administrator\Helper\GuidedtoursHelper;
use Joomla\Database\DatabaseQuery;
use Joomla\Database\ParameterType;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of steps records.
*
* @since 4.3.0
*/
class StepsModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @since 4.3.0
* @see \Joomla\CMS\MVC\Controller\BaseController
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'a.id',
'tour_id', 'a.tour_id',
'title', 'a.title',
'type', 'a.type',
'description', 'a.description',
'published', 'a.published',
'ordering', 'a.ordering',
'created_by', 'a.created_by',
'modified', 'a.modified',
'modified_by', 'a.modified_by',
'note', 'a.note',
];
}
parent::__construct($config);
}
/**
* Provide a query to be used to evaluate if this is an Empty State, can be overridden in the model to provide granular control.
*
* @return DatabaseQuery
*
* @since 4.3.0
*/
protected function getEmptyStateQuery()
{
$query = parent::getEmptyStateQuery();
$tourId = $this->getState('filter.tour_id');
if ($tourId) {
$tourId = (int) $tourId;
$query->where($this->getDatabase()->quoteName('a.tour_id') . ' = :tour_id')
->bind(':tour_id', $tourId, ParameterType::INTEGER);
}
return $query;
}
/**
* Method to auto-populate the model state.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 4.3.0
*/
protected function populateState($ordering = 'a.ordering', $direction = 'ASC')
{
$app = Factory::getApplication();
$tourId = $app->getUserStateFromRequest($this->context . '.filter.tour_id', 'tour_id', 0, 'int');
if (empty($tourId)) {
$tourId = $app->getUserState('com_guidedtours.tour_id');
}
$this->setState('filter.tour_id', $tourId);
// Keep the tour_id for adding new visits
$app->setUserState('com_guidedtours.tour_id', $tourId);
// List state information.
parent::populateState($ordering, $direction);
}
/**
* Method to get a store id based on model configuration state.
*
* This is necessary because the model is used by the component and
* different modules that might need different sets of data or different
* ordering requirements.
*
* @param string $id A prefix for the store id.
*
* @return string A store id.
*
* @since 4.3.0
*/
protected function getStoreId($id = '')
{
// Compile the store id.
$id .= ':' . $this->getState('filter.tour_id');
$id .= ':' . $this->getState('filter.search');
$id .= ':' . $this->getState('filter.published');
return parent::getStoreId($id);
}
/**
* Build an SQL query to load the list data.
*
* @return \Joomla\Database\DatabaseQuery
*
* @since 4.3.0
*/
protected function getListQuery()
{
// Create a new query object.
$db = $this->getDatabase();
$query = $db->getQuery(true);
// Select the required fields from the table.
$query->select(
$this->getState(
'list.select',
'a.*'
)
);
$query->from($db->quoteName('#__guidedtour_steps', 'a'));
// Join with user table
$query->select(
[
$db->quoteName('uc.name', 'editor'),
]
)
->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('a.checked_out'));
$tourId = $this->getState('filter.tour_id');
if (is_numeric($tourId)) {
$tourId = (int) $tourId;
$query->where($db->quoteName('a.tour_id') . ' = :tour_id')
->bind(':tour_id', $tourId, ParameterType::INTEGER);
} elseif (\is_array($tourId)) {
$tourId = ArrayHelper::toInteger($tourId);
$query->whereIn($db->quoteName('a.tour_id'), $tourId);
}
// Published state
$published = (string) $this->getState('filter.published');
if (is_numeric($published)) {
$query->where($db->quoteName('a.published') . ' = :published');
$query->bind(':published', $published, ParameterType::INTEGER);
} elseif ($published === '') {
$query->where('(' . $db->quoteName('a.published') . ' = 0 OR ' . $db->quoteName('a.published') . ' = 1)');
}
// Filter by search in title.
$search = $this->getState('filter.search');
if (!empty($search)) {
if (stripos($search, 'id:') === 0) {
$search = (int) substr($search, 3);
$query->where($db->quoteName('a.id') . ' = :search')
->bind(':search', $search, ParameterType::INTEGER);
} elseif (stripos($search, 'description:') === 0) {
$search = '%' . substr($search, 12) . '%';
$query->where('(' . $db->quoteName('a.description') . ' LIKE :search1)')
->bind([':search1'], $search);
} else {
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where(
'(' . $db->quoteName('a.title') . ' LIKE :search1'
. ' OR ' . $db->quoteName('a.description') . ' LIKE :search2'
. ' OR ' . $db->quoteName('a.note') . ' LIKE :search3)'
)
->bind([':search1', ':search2', ':search3'], $search);
}
}
// Add the list ordering clause.
$orderCol = $this->state->get('list.ordering', 'a.ordering');
$orderDirn = $this->state->get('list.direction', 'ASC');
$query->order($db->escape($orderCol) . ' ' . $db->escape($orderDirn));
return $query;
}
/**
* Method to get an array of data items.
*
* @return mixed An array of data items on success, false on failure.
*
* @since 4.3.0
*/
public function getItems()
{
$items = parent::getItems();
$tourLanguageLoaded = false;
foreach ($items as $item) {
if (!$tourLanguageLoaded) {
$app = Factory::getApplication();
$tourId = $item->tour_id;
/** @var \Joomla\Component\Guidedtours\Administrator\Model\TourModel $tourModel */
$tourModel = $app->bootComponent('com_guidedtours')
->getMVCFactory()->createModel('Tour', 'Administrator', [ 'ignore_request' => true ]);
$tour = $tourModel->getItem($tourId);
GuidedtoursHelper::loadTranslationFiles($tour->uid, true);
$tourLanguageLoaded = true;
}
$item->title = Text::_($item->title);
$item->description = Text::_($item->description);
}
return $items;
}
}

View File

@ -0,0 +1,500 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Component\Guidedtours\Administrator\Helper\GuidedtoursHelper;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for tour
*
* @since 4.3.0
*/
class TourModel extends AdminModel
{
/**
* The prefix to use with controller messages.
*
* @var string
* @since 4.3.0
*/
protected $text_prefix = 'COM_GUIDEDTOURS';
/**
* Type alias for content type
*
* @var string
* @since 4.3.0
*/
public $typeAlias = 'com_guidedtours.tour';
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*
* @since 4.3.0
*/
public function save($data)
{
$input = Factory::getApplication()->getInput();
// Language keys must include GUIDEDTOUR to prevent save issues
if (strpos($data['description'], 'GUIDEDTOUR') !== false) {
$data['description'] = strip_tags($data['description']);
}
if ($input->get('task') == 'save2copy') {
$origTable = clone $this->getTable();
$origTable->load($input->getInt('id'));
$data['published'] = 0;
}
// Set step language to parent tour language on save.
$id = $data['id'];
$lang = $data['language'];
$this->setStepsLanguage($id, $lang);
return parent::save($data);
}
/**
* Prepare and sanitise the table prior to saving.
*
* @param \Joomla\CMS\Table\Table $table The Table object
*
* @return void
*
* @since 4.3.0
*/
protected function prepareTable($table)
{
$date = Factory::getDate()->toSql();
$table->title = htmlspecialchars_decode($table->title, ENT_QUOTES);
if (empty($table->id)) {
// Set the values
$table->created = $date;
// Set ordering to the last item if not set
if (empty($table->ordering)) {
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select('MAX(ordering)')
->from($db->quoteName('#__guidedtours'));
$db->setQuery($query);
$max = $db->loadResult();
$table->ordering = $max + 1;
}
} else {
// Set the values
$table->modified = $date;
$table->modified_by = $this->getCurrentUser()->id;
}
}
/**
* Abstract method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return \JForm|boolean A JForm object on success, false on failure
*
* @since 4.3.0
*/
public function getForm($data = [], $loadData = true)
{
// Get the form.
$form = $this->loadForm(
'com_guidedtours.tour',
'tour',
[
'control' => 'jform',
'load_data' => $loadData,
]
);
if (empty($form)) {
return false;
}
$id = $data['id'] ?? $form->getValue('id');
$item = $this->getItem($id);
// Modify the form based on access controls.
if (!$this->canEditState((object) $item)) {
$form->setFieldAttribute('published', 'disabled', 'true');
$form->setFieldAttribute('published', 'required', 'false');
$form->setFieldAttribute('published', 'filter', 'unset');
}
$currentDate = Factory::getDate()->toSql();
$form->setFieldAttribute('created', 'default', $currentDate);
$form->setFieldAttribute('modified', 'default', $currentDate);
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 4.3.0
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState(
'com_guidedtours.edit.tour.data',
[]
);
if (empty($data)) {
$data = $this->getItem();
}
return $data;
}
/**
* Method to get a single record by id or uid
*
* @param integer|string $pk The id or uid of the tour.
*
* @return CMSObject|boolean Object on success, false on failure.
*
* @since 4.3.0
*/
public function getItem($pk = null)
{
$pk = (!empty($pk)) ? $pk : (int) $this->getState($this->getName() . '.id');
$table = $this->getTable();
if (\is_integer($pk)) {
$result = $table->load((int) $pk);
} else {
// Attempt to load the row by uid.
$result = $table->load([ 'uid' => $pk ]);
}
// Check for a table object error.
if ($result === false) {
// If there was no underlying error, then the false means there simply was not a row in the db for this $pk.
if (!$table->getError()) {
$this->setError(Text::_('JLIB_APPLICATION_ERROR_NOT_EXIST'));
} else {
$this->setError($table->getError());
}
return false;
}
// Convert to the CMSObject before adding other data.
$properties = $table->getProperties(1);
$item = ArrayHelper::toObject($properties, CMSObject::class);
if (property_exists($item, 'params')) {
$registry = new Registry($item->params);
$item->params = $registry->toArray();
}
if (!empty($item->uid)) {
GuidedtoursHelper::loadTranslationFiles($item->uid, true);
}
if (!empty($item->id)) {
$item->title_translation = Text::_($item->title);
$item->description_translation = Text::_($item->description);
}
return $item;
}
/**
* Delete all steps if a tour is deleted
*
* @param object $pks The primary key related to the tours.
*
* @return boolean
*
* @since 4.3.0
*/
public function delete(&$pks)
{
$pks = ArrayHelper::toInteger((array) $pks);
$table = $this->getTable();
// Include the plugins for the delete events.
PluginHelper::importPlugin($this->events_map['delete']);
// Iterate the items to delete each one.
foreach ($pks as $i => $pk) {
if ($table->load($pk)) {
if ($this->canDelete($table)) {
$context = $this->option . '.' . $this->name;
// Trigger the before delete event.
$result = Factory::getApplication()->triggerEvent($this->event_before_delete, [$context, $table]);
if (\in_array(false, $result, true)) {
$this->setError($table->getError());
return false;
}
$tourId = $table->id;
if (!$table->delete($pk)) {
$this->setError($table->getError());
return false;
}
// Delete of the tour has been successful, now delete the steps
$db = $this->getDatabase();
$query = $db->getQuery(true)
->delete($db->quoteName('#__guidedtour_steps'))
->where($db->quoteName('tour_id') . '=' . $tourId);
$db->setQuery($query);
$db->execute();
// Trigger the after event.
Factory::getApplication()->triggerEvent($this->event_after_delete, [$context, $table]);
} else {
// Prune items that you can't change.
unset($pks[$i]);
$error = $this->getError();
if ($error) {
Log::add($error, Log::WARNING, 'jerror');
return false;
}
Log::add(Text::_('JLIB_APPLICATION_ERROR_DELETE_NOT_PERMITTED'), Log::WARNING, 'jerror');
return false;
}
} else {
$this->setError($table->getError());
return false;
}
}
// Clear the component's cache
$this->cleanCache();
return true;
}
/**
* Duplicate all steps if a tour is duplicated
*
* @param object $pks The primary key related to the tours.
*
* @return boolean
*
* @since 4.3.0
*/
public function duplicate(&$pks)
{
$user = $this->getCurrentUser();
$db = $this->getDatabase();
// Access checks.
if (!$user->authorise('core.create', 'com_guidedtours')) {
throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED'));
}
$table = $this->getTable();
$date = Factory::getDate()->toSql();
foreach ($pks as $pk) {
if ($table->load($pk, true)) {
// Reset the id to create a new record.
$table->id = 0;
$table->published = 0;
if (!$table->check() || !$table->store()) {
throw new \Exception($table->getError());
}
$pk = (int) $pk;
$query = $db->getQuery(true)
->select(
$db->quoteName(
[
'title',
'description',
'ordering',
'position',
'target',
'type',
'interactive_type',
'url',
'created',
'modified',
'checked_out_time',
'checked_out',
'language',
'note',
]
)
)
->from($db->quoteName('#__guidedtour_steps'))
->where($db->quoteName('tour_id') . ' = :id')
->bind(':id', $pk, ParameterType::INTEGER);
$db->setQuery($query);
$rows = $db->loadObjectList();
$query = $db->getQuery(true)
->insert($db->quoteName('#__guidedtour_steps'))
->columns(
[
$db->quoteName('tour_id'),
$db->quoteName('title'),
$db->quoteName('description'),
$db->quoteName('ordering'),
$db->quoteName('position'),
$db->quoteName('target'),
$db->quoteName('type'),
$db->quoteName('interactive_type'),
$db->quoteName('url'),
$db->quoteName('created'),
$db->quoteName('created_by'),
$db->quoteName('modified'),
$db->quoteName('modified_by'),
$db->quoteName('language'),
$db->quoteName('note'),
]
);
foreach ($rows as $step) {
$dataTypes = [
ParameterType::INTEGER,
ParameterType::STRING,
ParameterType::STRING,
ParameterType::INTEGER,
ParameterType::STRING,
ParameterType::STRING,
ParameterType::INTEGER,
ParameterType::INTEGER,
ParameterType::STRING,
ParameterType::STRING,
ParameterType::INTEGER,
ParameterType::STRING,
ParameterType::INTEGER,
ParameterType::STRING,
ParameterType::STRING,
];
$query->values(
implode(
',',
$query->bindArray(
[
$table->id,
$step->title,
$step->description,
$step->ordering,
$step->position,
$step->target,
$step->type,
$step->interactive_type,
$step->url,
$date,
$user->id,
$date,
$user->id,
$step->language,
$step->note,
],
$dataTypes
)
)
);
}
$db->setQuery($query);
try {
$db->execute();
} catch (\RuntimeException $e) {
Factory::getApplication()->enqueueMessage($e->getMessage(), 'error');
return false;
}
} else {
throw new \Exception($table->getError());
}
}
// Clear tours cache
$this->cleanCache();
return true;
}
/**
* Sets a tour's steps language
*
* @param int $id Id of a tour
* @param string $language The language to apply to the steps belong the tour
*
* @return boolean
*
* @since 4.3.0
*/
protected function setStepsLanguage(int $id, string $language = '*'): bool
{
if ($id <= 0) {
return false;
}
$db = $this->getDatabase();
$query = $db->getQuery(true)
->update($db->quoteName('#__guidedtour_steps'))
->set($db->quoteName('language') . ' = :language')
->where($db->quoteName('tour_id') . ' = :tourId')
->bind(':language', $language)
->bind(':tourId', $id, ParameterType::INTEGER);
return $db->setQuery($query)
->execute();
}
}

View File

@ -0,0 +1,264 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Model;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Component\Guidedtours\Administrator\Helper\GuidedtoursHelper;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Model class for Tours
*
* @since 4.3.0
*/
class ToursModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
*
* @see JController
* @since 4.3.0
*/
public function __construct($config = [])
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'a.id',
'title', 'a.title',
'access', 'a.access', 'access_level',
'description', 'a.description',
'published', 'a.published',
'language', 'a.language',
'ordering', 'a.ordering',
'extensions', 'a.extensions',
'created_by', 'a.created_by',
'modified', 'a.modified',
'modified_by', 'a.modified_by',
'note', 'a.note',
];
}
parent::__construct($config);
}
/**
* Method to auto-populate the model state.
*
* This method should only be called once per instantiation and is designed
* to be called on the first call to the getState() method unless the model
* configuration flag to ignore the request is set.
*
* Note. Calling getState in this method will result in recursion.
*
* @param string $ordering An optional ordering field.
* @param string $direction An optional direction (asc|desc).
*
* @return void
*
* @since 4.3.0
*/
protected function populateState($ordering = 'a.ordering', $direction = 'ASC')
{
$app = Factory::getApplication();
$extension = $app->getUserStateFromRequest($this->context . '.filter.extension', 'extension', null, 'cmd');
$this->setState('filter.extension', $extension);
parent::populateState($ordering, $direction);
}
/**
* Get the filter form
*
* @param array $data data
* @param boolean $loadData load current data
*
* @return \JForm|false the JForm object or false
*
* @since 4.3.0
*/
public function getFilterForm($data = [], $loadData = true)
{
$form = parent::getFilterForm($data, $loadData);
if ($form) {
$form->setValue('extension', null, $this->getState('filter.extension'));
}
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return string The query to database.
*
* @since 4.3.0
*/
public function getListQuery()
{
// Create a new query object.
$db = $this->getDatabase();
$query = $db->getQuery(true);
// Select the required fields from the table.
$query->select(
$this->getState(
'list.select',
'a.*'
)
);
$subQuery = $db->getQuery(true)
->select('COUNT(' . $db->quoteName('s.id') . ')')
->from($db->quoteName('#__guidedtour_steps', 's'))
->where(
[
$db->quoteName('s.tour_id') . ' = ' . $db->quoteName('a.id'),
$db->quoteName('s.published') . ' = 1',
]
);
$query->select('(' . $subQuery . ') AS ' . $db->quoteName('steps_count'))
->from($db->quoteName('#__guidedtours', 'a'));
// Join with user table
$query->select(
[
$db->quoteName('uc.name', 'editor'),
]
)
->join('LEFT', $db->quoteName('#__users', 'uc'), $db->quoteName('uc.id') . ' = ' . $db->quoteName('a.checked_out'));
// Join with language table
$query->select(
[
$db->quoteName('l.title', 'language_title'),
$db->quoteName('l.image', 'language_image'),
]
)
->join('LEFT', $db->quoteName('#__languages', 'l'), $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.language'));
// Join access table
$query->select(
[
$db->quoteName('ag.title', 'access_level'),
]
)
->join('LEFT', $db->quoteName('#__viewlevels', 'ag'), $db->quoteName('ag.id') . ' = ' . $db->quoteName('a.access'));
// Filter by extension
if ($extension = $this->getState('filter.extension')) {
$extension = '%' . $extension . '%';
$all = '%*%';
$query->where(
'(' . $db->quoteName('a.extensions') . ' LIKE :all OR ' .
$db->quoteName('a.extensions') . ' LIKE :extensions)'
)
->bind([':all'], $all)
->bind([':extensions'], $extension);
}
$status = (string) $this->getState('filter.published');
// Filter by status
if (is_numeric($status)) {
$status = (int) $status;
$query->where($db->quoteName('a.published') . ' = :published')
->bind(':published', $status, ParameterType::INTEGER);
} elseif ($status === '') {
$query->where($db->quoteName('a.published') . ' IN (0, 1)');
}
// Filter by access level.
$access = $this->getState('filter.access');
if (is_numeric($access)) {
$access = (int) $access;
$query->where($db->quoteName('a.access') . ' = :access')
->bind(':access', $access, ParameterType::INTEGER);
} elseif (\is_array($access)) {
$access = ArrayHelper::toInteger($access);
$query->whereIn($db->quoteName('a.access'), $access);
}
// Filter on the language, or all.
if ($language = $this->getState('filter.language')) {
$language = (array) $language;
$query->whereIn($db->quoteName('a.language'), $language, ParameterType::STRING);
}
// Filter by search in title.
$search = $this->getState('filter.search');
if (!empty($search)) {
if (stripos($search, 'id:') === 0) {
$search = (int) substr($search, 3);
$query->where($db->quoteName('a.id') . ' = :search')
->bind(':search', $search, ParameterType::INTEGER);
} elseif (stripos($search, 'description:') === 0) {
$search = '%' . substr($search, 12) . '%';
$query->where('(' . $db->quoteName('a.description') . ' LIKE :search1)')
->bind([':search1'], $search);
} else {
$search = '%' . str_replace(' ', '%', trim($search)) . '%';
$query->where(
'(' . $db->quoteName('a.title') . ' LIKE :search1'
. ' OR ' . $db->quoteName('a.description') . ' LIKE :search2'
. ' OR ' . $db->quoteName('a.note') . ' LIKE :search3)'
)
->bind([':search1', ':search2', ':search3'], $search);
}
}
// Add the list ordering clause.
$orderCol = $this->state->get('list.ordering', 'a.ordering');
$orderDirn = strtoupper($this->state->get('list.direction', 'ASC'));
$query->order($db->escape($orderCol) . ' ' . ($orderDirn === 'DESC' ? 'DESC' : 'ASC'));
return $query;
}
/**
* Method to get an array of data items.
*
* @return mixed An array of data items on success, false on failure.
*
* @since 4.3.0
*/
public function getItems()
{
$items = parent::getItems();
foreach ($items as & $item) {
if (!empty($item->uid)) {
GuidedtoursHelper::loadTranslationFiles($item->uid, false);
}
$item->title = Text::_($item->title);
$item->description = Text::_($item->description);
$item->extensions = (new Registry($item->extensions))->toArray();
}
unset($item);
return $items;
}
}

View File

@ -0,0 +1,94 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Table;
use Joomla\CMS\Factory;
use Joomla\CMS\Table\Table;
use Joomla\CMS\User\CurrentUserInterface;
use Joomla\CMS\User\CurrentUserTrait;
use Joomla\Database\DatabaseDriver;
use Joomla\Event\DispatcherInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Step table class.
*
* @since 4.3.0
*/
class StepTable extends Table implements CurrentUserInterface
{
use CurrentUserTrait;
/**
* Indicates that columns fully support the NULL value in the database
*
* @var boolean
* @since 4.3.0
*/
protected $_supportNullValue = true;
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 4.3.0
*/
public function __construct(DatabaseDriver $db, DispatcherInterface $dispatcher = null)
{
parent::__construct('#__guidedtour_steps', 'id', $db, $dispatcher);
}
/**
* Stores a step.
*
* @param boolean $updateNulls True to update fields even if they are null.
*
* @return boolean True on success, false on failure.
*
* @since 4.3.0
*/
public function store($updateNulls = true)
{
$date = Factory::getDate()->toSql();
$userId = $this->getCurrentUser()->id;
// Set created date if not set.
if (!(int) $this->created) {
$this->created = $date;
}
if ($this->id) {
// Existing item
$this->modified_by = $userId;
$this->modified = $date;
} else {
// Field created_by field can be set by the user, so we don't touch it if it's set.
if (empty($this->created_by)) {
$this->created_by = $userId;
}
if (!(int) $this->modified) {
$this->modified = $date;
}
if (empty($this->modified_by)) {
$this->modified_by = $userId;
}
}
return parent::store($updateNulls);
}
}

View File

@ -0,0 +1,193 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\Table;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Table\Table;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\CurrentUserInterface;
use Joomla\CMS\User\CurrentUserTrait;
use Joomla\Database\DatabaseDriver;
use Joomla\Event\DispatcherInterface;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Tours table class.
*
* @since 4.3.0
*/
class TourTable extends Table implements CurrentUserInterface
{
use CurrentUserTrait;
/**
* Indicates that columns fully support the NULL value in the database
*
* @var boolean
* @since 4.3.0
*/
protected $_supportNullValue = true;
/**
* An array of key names to be json encoded in the bind function
*
* @var array
* @since 4.3.0
*/
protected $_jsonEncode = ['extensions'];
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 4.3.0
*/
public function __construct(DatabaseDriver $db, DispatcherInterface $dispatcher = null)
{
parent::__construct('#__guidedtours', 'id', $db, $dispatcher);
}
/**
* Stores a tour.
*
* @param boolean $updateNulls True to update extensions even if they are null.
*
* @return boolean True on success, false on failure.
*
* @since 4.3.0
*/
public function store($updateNulls = true)
{
$date = Factory::getDate()->toSql();
$userId = $this->getCurrentUser()->id;
// Set created date if not set.
if (!(int) $this->created) {
$this->created = $date;
}
if ($this->id) {
// Existing item
$this->modified_by = $userId;
$this->modified = $date;
} else {
// Field created_by field can be set by the user, so we don't touch it if it's set.
if (empty($this->created_by)) {
$this->created_by = $userId;
}
if (!(int) $this->modified) {
$this->modified = $date;
}
if (empty($this->modified_by)) {
$this->modified_by = $userId;
}
}
if (empty($this->extensions)) {
$this->extensions = ["*"];
}
// set missing Uid
if (empty($this->uid)) {
$this->setTourUid();
}
// make sure the uid is unique
$this->ensureUniqueUid();
return parent::store($updateNulls);
}
/**
* Method to set the uid when empty
*
* @return string $uid Contains the non-empty uid.
*
* @since 5.0.0
*/
protected function setTourUid()
{
// Tour follows Joomla naming convention
if (str_starts_with($this->title, 'COM_GUIDEDTOURS_TOUR_') && str_ends_with($this->title, '_TITLE')) {
$uidTitle = 'joomla_' . str_replace('COM_GUIDEDTOURS_TOUR_', '', $this->title);
// Remove the last _TITLE part
$pos = strrpos($uidTitle, "_TITLE");
if ($pos !== false) {
$uidTitle = substr($uidTitle, 0, $pos);
}
} elseif (preg_match('#COM_(\w+)_TOUR_#', $this->title) && str_ends_with($this->title, '_TITLE')) {
// Tour follows component naming pattern
$uidTitle = preg_replace('#COM_(\w+)_TOUR_#', '$1.', $this->title);
// Remove the last _TITLE part
$pos = strrpos($uidTitle, "_TITLE");
if ($pos !== false) {
$uidTitle = substr($uidTitle, 0, $pos);
}
} else {
$uri = Uri::getInstance();
$host = $uri->toString(['host']);
$host = ApplicationHelper::stringURLSafe($host, $this->language);
$uidTitle = $host . ' ' . str_replace('COM_GUIDEDTOURS_TOUR_', '', $this->title);
// Remove the last _TITLE part
if (str_ends_with($uidTitle, '_TITLE')) {
$pos = strrpos($uidTitle, '_TITLE');
$uidTitle = substr($uidTitle, 0, $pos);
}
}
// ApplicationHelper::stringURLSafe will replace a period (.) separator so we split the construction into multiple parts
$uidTitleParts = explode('.', $uidTitle);
array_walk($uidTitleParts, function (&$value, $key, $tourLanguage) {
$value = ApplicationHelper::stringURLSafe($value, $tourLanguage);
}, $this->language);
$this->uid = implode('.', $uidTitleParts);
$this->store();
return $this->uid;
}
/**
* Method to change the uid when not unique.
*
* @return string $uid Contains the modified uid.
*
* @since 5.0.0
*/
protected function ensureUniqueUid()
{
$table = new TourTable($this->_db);
$unique = false;
// Alter the title & uid
while (!$unique) {
// Attempt to load the row by uid.
$uidItem = $table->load([ 'uid' => $this->uid ]);
if ($uidItem && $table->id > 0 && $table->id != $this->id) {
$this->uid = StringHelper::increment($this->uid, 'dash');
} else {
$unique = true;
}
}
return $this->uid;
}
}

View File

@ -0,0 +1,156 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\View\Step;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View to edit an Step
*
* @since 4.3.0
*/
class HtmlView extends BaseHtmlView
{
/**
* The \JForm object
*
* @var \Joomla\CMS\Form\Form
*/
protected $form;
/**
* The active item
*
* @var object
*/
protected $item;
/**
* The model state
*
* @var object
*/
protected $state;
/**
* The actions the user is authorised to perform
*
* @var \Joomla\CMS\Object\CMSObject
*/
protected $canDo;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*
* @throws \Exception
* @since 4.3.0
*/
public function display($tpl = null)
{
$this->form = $this->get('Form');
$this->item = $this->get('Item');
$this->state = $this->get('State');
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
$this->addToolbar();
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @throws \Exception
* @since 4.3.0
*/
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
$user = $this->getCurrentUser();
$userId = $user->id;
$isNew = empty($this->item->id);
$canDo = ContentHelper::getActions('com_guidedtours');
ToolbarHelper::title(Text::_($isNew ? 'COM_GUIDEDTOURS_MANAGER_STEP_NEW' : 'COM_GUIDEDTOURS_MANAGER_STEP_EDIT'), 'map-signs');
$toolbarButtons = [];
if ($isNew) {
// For new records, check the create permission.
if ($canDo->get('core.create')) {
ToolbarHelper::apply('step.apply');
$toolbarButtons = [['save', 'step.save'], ['save2new', 'step.save2new']];
}
ToolbarHelper::saveGroup(
$toolbarButtons,
'btn-success'
);
ToolbarHelper::cancel(
'step.cancel'
);
} else {
// Since it's an existing record, check the edit permission, or fall back to edit own if the owner.
$itemEditable = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $userId);
if ($itemEditable) {
ToolbarHelper::apply('step.apply');
$toolbarButtons = [['save', 'step.save']];
// We can save this record, but check the create permission to see if we can return to make a new one.
if ($canDo->get('core.create')) {
$toolbarButtons[] = ['save2new', 'step.save2new'];
$toolbarButtons[] = ['save2copy', 'step.save2copy'];
}
ToolbarHelper::saveGroup(
$toolbarButtons,
'btn-success'
);
ToolbarHelper::cancel(
'step.cancel',
'JTOOLBAR_CLOSE'
);
}
}
ToolbarHelper::divider();
$inlinehelp = (string) $this->form->getXml()->config->inlinehelp['button'] === 'show';
$targetClass = (string) $this->form->getXml()->config->inlinehelp['targetclass'] ?: 'hide-aware-inline-help';
if ($inlinehelp) {
ToolbarHelper::inlinehelp($targetClass);
}
ToolbarHelper::help('Guided_Tours:_New_or_Edit_Step');
}
}

View File

@ -0,0 +1,204 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\View\Steps;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View class for a list of guidedtour_steps.
*
* @since 4.3.0
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*/
public $activeFilters;
/**
* Is this view an Empty State
*
* @var boolean
*
* @since 4.3.0
*/
private $isEmptyState = false;
/**
* Display the view.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
public function display($tpl = null)
{
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
if (!\count($this->items) && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
if ($this->state->get('filter.tour_id', -1) < 0) {
// This arises when you are logged out and return to the steps view after logging back in
// We redirect back to the tour lists view
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_GUIDEDTOURS_STEPS_UNKNOWN_TOUR'), 'notice');
$app->redirect(Route::_('index.php?option=com_guidedtours&view=tours', false), 300);
return;
}
// Unset the tour_id field from activeFilters as we don't filter by tour here.
unset($this->activeFilters['tour_id']);
$this->addToolbar();
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 4.3.0
*/
protected function addToolbar()
{
// Get the toolbar object instance
$toolbar = Toolbar::getInstance('toolbar');
$canDo = ContentHelper::getActions('com_guidedtours');
$app = Factory::getApplication();
$user = $app->getIdentity();
/** @var \Joomla\Component\Guidedtours\Administrator\Model\TourModel $tourModel */
$tourModel = $app->bootComponent('com_guidedtours')
->getMVCFactory()->createModel('Tour', 'Administrator', ['ignore_request' => true]);
$tour = $tourModel->getItem($this->state->get('filter.tour_id', -1));
$title = !empty($tour->title) ? $tour->title : '';
ToolbarHelper::title(Text::sprintf('COM_GUIDEDTOURS_STEPS_LIST', Text::_($title)), 'map-signs');
$arrow = $this->getLanguage()->isRtl() ? 'arrow-right' : 'arrow-left';
ToolbarHelper::link(
Route::_('index.php?option=com_guidedtours&view=tours'),
'JTOOLBAR_BACK',
$arrow
);
if ($canDo->get('core.create')) {
$toolbar->addNew('step.add');
}
if (!$this->isEmptyState && $canDo->get('core.edit.state')) {
$dropdown = $toolbar->dropdownButton('status-group')
->text('JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
$childBar->publish('steps.publish')->listCheck(true);
$childBar->unpublish('steps.unpublish')->listCheck(true);
$childBar->archive('steps.archive')->listCheck(true);
$childBar->checkin('steps.checkin')->listCheck(true);
if ($this->state->get('filter.published') != -2) {
$childBar->trash('steps.trash')->listCheck(true);
}
}
if (!$this->isEmptyState && $this->state->get('filter.published') == -2 && $canDo->get('core.delete')) {
$toolbar->delete('steps.delete')
->text('JTOOLBAR_EMPTY_TRASH')
->message('JGLOBAL_CONFIRM_DELETE')
->listCheck(true);
}
if ($user->authorise('core.admin', 'com_guidedtours') || $user->authorise('core.options', 'com_guidedtours')) {
$toolbar->preferences('com_guidedtours');
}
ToolbarHelper::help('Guided_Tours:_Steps');
}
/**
* Returns an array of fields the table can be sorted by
*
* @return array Array containing the field name to sort by as the key and display text as value
*
* @since 4.3.0
*/
protected function getSortFields()
{
return [
'a.id' => Text::_('JGRID_HEADING_ID'),
];
}
}

View File

@ -0,0 +1,158 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\View\Tour;
use Joomla\CMS\Factory;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View to edit an tour.
*
* @since 4.3.0
*/
class HtmlView extends BaseHtmlView
{
/**
* The \JForm object
*
* @var \Joomla\CMS\Form\Form
*/
protected $form;
/**
* The active item
*
* @var object
*/
protected $item;
/**
* The model state
*
* @var object
*/
protected $state;
/**
* The actions the user is authorised to perform
*
* @var \Joomla\Registry\Registry
*/
protected $canDo;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*
* @throws \Exception
* @since 4.3.0
*/
public function display($tpl = null)
{
$this->form = $this->get('Form');
$this->item = $this->get('Item');
$this->state = $this->get('State');
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
$this->addToolbar();
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @throws \Exception
* @since 4.3.0
*/
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
$user = $this->getCurrentUser();
$userId = $user->id;
$isNew = empty($this->item->id);
$canDo = ContentHelper::getActions('com_guidedtours');
ToolbarHelper::title(Text::_($isNew ? 'COM_GUIDEDTOURS_MANAGER_TOUR_NEW' : 'COM_GUIDEDTOURS_MANAGER_TOUR_EDIT'), 'map-signs');
$toolbarButtons = [];
if ($isNew) {
// For new records, check the create permission.
if ($canDo->get('core.create')) {
ToolbarHelper::apply('tour.apply');
$toolbarButtons = [['save', 'tour.save'], ['save2new', 'tour.save2new']];
}
ToolbarHelper::saveGroup(
$toolbarButtons,
'btn-success'
);
ToolbarHelper::cancel(
'tour.cancel'
);
} else {
// Since it's an existing record, check the edit permission, or fall back to edit own if the owner.
$itemEditable = $canDo->get('core.edit') || ($canDo->get('core.edit.own') && $this->item->created_by == $userId);
if ($itemEditable) {
ToolbarHelper::apply('tour.apply');
$toolbarButtons = [['save', 'tour.save']];
// We can save this record, but check the create permission to see if we can return to make a new one.
if ($canDo->get('core.create')) {
$toolbarButtons[] = ['save2new', 'tour.save2new'];
$toolbarButtons[] = ['save2copy', 'tour.save2copy'];
}
ToolbarHelper::saveGroup(
$toolbarButtons,
'btn-success'
);
ToolbarHelper::cancel(
'tour.cancel',
'JTOOLBAR_CLOSE'
);
}
}
ToolbarHelper::divider();
$inlinehelp = (string) $this->form->getXml()->config->inlinehelp['button'] === 'show';
$targetClass = (string) $this->form->getXml()->config->inlinehelp['targetclass'] ?: 'hide-aware-inline-help';
if ($inlinehelp) {
ToolbarHelper::inlinehelp($targetClass);
}
ToolbarHelper::help('Guided_Tours:_New_or_Edit_Tour');
}
}

View File

@ -0,0 +1,174 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Component\Guidedtours\Administrator\View\Tours;
use Joomla\CMS\Helper\ContentHelper;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\GenericDataException;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View class for a list of guidedtours.
*
* @since 4.3.0
*/
class HtmlView extends BaseHtmlView
{
/**
* An array of items
*
* @var array
*/
protected $items;
/**
* The pagination object
*
* @var \Joomla\CMS\Pagination\Pagination
*/
protected $pagination;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*/
public $filterForm;
/**
* The active search filters
*
* @var array
*/
public $activeFilters;
/**
* Is this view an Empty State
*
* @var boolean
*
* @since 4.3.0
*/
private $isEmptyState = false;
/**
* Display the view.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return void
*/
public function display($tpl = null)
{
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
if (!\count($this->items) && $this->isEmptyState = $this->get('IsEmptyState')) {
$this->setLayout('emptystate');
}
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
throw new GenericDataException(implode("\n", $errors), 500);
}
$this->addToolbar();
// We do not need to filter by language when multilingual is disabled
if (!Multilanguage::isEnabled()) {
unset($this->activeFilters['language']);
$this->filterForm->removeField('language', 'filter');
}
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @return void
*
* @since 4.3.0
*/
protected function addToolbar()
{
// Get the toolbar object instance
$toolbar = Toolbar::getInstance('toolbar');
ToolbarHelper::title(Text::_('COM_GUIDEDTOURS_TOURS_LIST'), 'map-signs');
$canDo = ContentHelper::getActions('com_guidedtours');
$user = $this->getCurrentUser();
if ($canDo->get('core.create')) {
$toolbar->addNew('tour.add');
}
if (!$this->isEmptyState && $canDo->get('core.edit.state')) {
$dropdown = $toolbar->dropdownButton('status-group')
->text('JTOOLBAR_CHANGE_STATUS')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
$childBar->publish('tours.publish')->listCheck(true);
$childBar->unpublish('tours.unpublish')->listCheck(true);
$childBar->archive('tours.archive')->listCheck(true);
$childBar->checkin('tours.checkin')->listCheck(true);
if ($this->state->get('filter.published') != -2) {
$childBar->trash('tours.trash')->listCheck(true);
}
if ($canDo->get('core.create')) {
$childBar->standardButton('copy')
->text('JTOOLBAR_DUPLICATE')
->task('tours.duplicate')
->listCheck(true);
}
}
if (!$this->isEmptyState && $this->state->get('filter.published') == -2 && $canDo->get('core.delete')) {
$toolbar->delete('tours.delete')
->text('JTOOLBAR_EMPTY_TRASH')
->message('JGLOBAL_CONFIRM_DELETE')
->listCheck(true);
}
if ($user->authorise('core.admin', 'com_guidedtours') || $user->authorise('core.options', 'com_guidedtours')) {
$toolbar->preferences('com_guidedtours');
}
ToolbarHelper::help('Guided_Tours');
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\MVC\View\GenericDataException;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate');
if (empty($this->item->tour_id)) {
throw new GenericDataException("\nThe Tour id was not set!\n", 500);
}
$lang = $this->getLanguage()->getTag();
?>
<form action="<?php echo Route::_('index.php?option=com_guidedtours&view=step&layout=edit&id=' .
(int) $this->item->id); ?>" method="post" name="adminForm" id="guidedtour-dates-form" class="form-validate">
<?php echo LayoutHelper::render('joomla.edit.title_alias', $this); ?>
<?php if ($this->item->id != 0 && strpos($this->item->title, 'GUIDEDTOUR') !== false) : ?>
<div class="row title-alias form-vertical mb-3">
<div class="col-12">
<?php $this->form->setFieldAttribute('title_translation', 'label', Text::sprintf('COM_GUIDEDTOURS_STEP_TITLE_TRANSLATION', $lang)); ?>
<?php echo $this->form->renderField('title_translation'); ?>
</div>
</div>
<?php endif; ?>
<div class="main-card">
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'details', 'recall' => true, 'breakpoint' => 768]); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'details', empty($this->item->id) ? Text::_('COM_GUIDEDTOURS_STEP_NEW_STEP') : Text::_('COM_GUIDEDTOURS_STEP_EDIT_STEP')); ?>
<div class="row">
<div class="col-lg-9">
<?php echo $this->form->renderField('description'); ?>
<?php if ($this->item->id != 0 && strpos($this->item->description, 'GUIDEDTOUR') !== false) : ?>
<?php $this->form->setFieldAttribute('description_translation', 'label', Text::sprintf('COM_GUIDEDTOURS_STEP_DESCRIPTION_TRANSLATION', $lang)); ?>
<?php echo $this->form->renderField('description_translation'); ?>
<?php endif; ?>
</div>
<div class="col-lg-3">
<?php
// Set main fields.
$this->fields = [
'published',
'language',
'position',
'target',
'type',
'url',
'interactive_type',
'note',
];
$this->hidden_fields = [
'tour_id',
];
echo LayoutHelper::render('joomla.edit.global', $this); ?>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'publishing', Text::_('JGLOBAL_FIELDSET_PUBLISHING')); ?>
<div class="row">
<div class="col-12 col-lg-8">
<fieldset id="fieldset-publishingdata" class="options-form">
<legend><?php echo Text::_('JGLOBAL_FIELDSET_PUBLISHING'); ?></legend>
<div>
<?php
$this->fields = [];
$this->hidden_fields = [];
echo LayoutHelper::render('joomla.edit.publishingdata', $this); ?>
</div>
</fieldset>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
</div>
<input type="hidden" name="task" value="">
<?php echo $this->form->getInput('tour_id'); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>

View File

@ -0,0 +1,234 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Helpers\StringHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
use Joomla\Component\Guidedtours\Administrator\Extension\GuidedtoursComponent;
use Joomla\Component\Guidedtours\Administrator\View\Steps\HtmlView;
/** @var HtmlView $this */
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('table.columns')
->useScript('multiselect');
$user = $this->getCurrentUser();
$userId = $user->get('id');
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$saveOrder = $listOrder == 'a.ordering';
$section = null;
$mode = false;
$tourId = $this->state->get('filter.tour_id');
$canEdit = $user->authorise('core.edit', 'com_guidedtours');
$canEditOwnTour = $user->authorise('core.edit.own', 'com_guidedtours');
$canEditStateTour = $user->authorise('core.edit.state', 'com_guidedtours');
$hasCheckinPermission = $user->authorise('core.manage', 'com_checkin');
if ($saveOrder && !empty($this->items)) {
$saveOrderingUrl = 'index.php?option=com_guidedtours&task=steps.saveOrderAjax&tmpl=component&' . Session::getFormToken() . '=1';
HTMLHelper::_('draggablelist.draggable');
}
?>
<form action="<?php echo Route::_('index.php?option=com_guidedtours&view=steps&tour_id=' . $tourId); ?>"
method="post" name="adminForm" id="adminForm">
<div id="j-main-container" class="j-main-container">
<?php
// Search tools bar
echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this]);
?>
<!-- If no steps -->
<?php if (empty($this->items)) :
?>
<!-- No steps -->
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span>
<span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('JGLOBAL_NO_MATCHING_RESULTS'); ?>
</div>
<?php endif; ?>
<!-- If there are steps, we start with the table -->
<?php if (!empty($this->items)) :
?>
<!-- Steps table starts here -->
<table class="table" id="stepsList">
<caption class="visually-hidden">
<?php echo Text::_('COM_GUIDEDTOURS_STEPS_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?></span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<!-- Steps table header -->
<thead>
<tr>
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<!-- Ordering?-->
<th scope="col" class="w-3 text-center d-none d-md-table-cell">
<?php echo HTMLHelper::_(
'searchtools.sort',
'',
'a.ordering',
$listDirn,
$listOrder,
null,
'asc',
'JGRID_HEADING_ORDERING',
'icon-sort'
); ?>
</th>
<th scope="col" class="w-1 text-center">
<?php echo HTMLHelper::_(
'searchtools.sort',
'JSTATUS',
'a.published',
$listDirn,
$listOrder
); ?>
</th>
<th scope="col">
<?php echo Text::_('COM_GUIDEDTOURS_STEP_TITLE'); ?>
</th>
<th scope="col" class="d-none d-md-table-cell">
<?php echo Text::_('COM_GUIDEDTOURS_DESCRIPTION'); ?>
</th>
<th scope="col" class="d-none d-md-table-cell">
<?php echo HTMLHelper::_(
'searchtools.sort',
'COM_GUIDEDTOURS_STEP_TYPE',
'a.type',
$listDirn,
$listOrder
); ?>
</th>
<th scope="col" class="w-10 text-center d-none d-md-table-cell">
<?php echo HTMLHelper::_(
'searchtools.sort',
'JGRID_HEADING_ID',
'a.id',
$listDirn,
$listOrder
); ?>
</th>
</tr>
</thead>
<!-- Table body begins -->
<tbody <?php if ($saveOrder) : ?>
class="js-draggable" data-url="<?php echo $saveOrderingUrl; ?>" data-direction="<?php echo strtolower($listDirn); ?>" data-nested="true" <?php
endif; ?>>
<?php foreach ($this->items as $i => $item) :
$canEditOwn = $canEditOwnTour && $item->created_by == $userId;
$canCheckin = $hasCheckinPermission || $item->checked_out == $userId || is_null($item->checked_out);
$canChange = $canEditStateTour && $canCheckin;
?>
<!-- Row begins -->
<tr class="row<?php echo $i % 2; ?>" data-draggable-group="none">
<!-- Item Checkbox -->
<td class="text-center">
<?php echo HTMLHelper::_('grid.id', $i, $item->id, false, 'cid', 'cb', $item->title); ?>
</td>
<!-- Draggable handle -->
<td class="text-center d-none d-md-table-cell">
<?php
$iconClass = '';
if (!$canChange) {
$iconClass = ' inactive';
} elseif (!$saveOrder) {
$iconClass = ' inactive" title="' . Text::_('JORDERINGDISABLED');
}
?>
<span class="sortable-handler <?php echo $iconClass; ?>">
<span class="icon-ellipsis-v" aria-hidden="true"></span>
</span>
<?php if ($canChange && $saveOrder) : ?>
<input type="text" class="hidden text-area-order" name="order[]" size="5" value="<?php echo $item->ordering; ?>">
<?php endif; ?>
</td>
<!-- Step State -->
<td class="text-center">
<?php echo HTMLHelper::_(
'jgrid.published',
$item->published,
$i,
'steps.',
$canChange
); ?>
</td>
<!-- Step name, edit link, and note -->
<th scope="row">
<div>
<?php if ($item->checked_out) : ?>
<?php echo HTMLHelper::_('jgrid.checkedout', $i, $item->editor, $item->checked_out_time, 'steps.', $canCheckin); ?>
<?php endif; ?>
<?php if ($canEdit || $canEditOwn) : ?>
<a href="<?php echo Route::_('index.php?option=com_guidedtours&task=step.edit&id=' . $item->id); ?> " title="<?php echo Text::_('JACTION_EDIT'); ?> <?php echo $this->escape($item->title); ?>">
<?php echo $this->escape($item->title); ?>
</a>
<?php else : ?>
<?php echo $this->escape($item->title); ?>
<?php endif; ?>
<?php if ($item->note) : ?>
<div class="small break-word">
<?php echo Text::sprintf('JGLOBAL_LIST_NOTE', $this->escape($item->note)); ?>
</div>
<?php endif; ?>
</div>
</th>
<td class="d-none d-md-table-cell">
<?php echo StringHelper::truncate($item->description, 200, true, false); ?>
</td>
<!-- Step Type -->
<td class="d-none d-md-table-cell">
<?php echo Text::_(GuidedtoursComponent::STEP_TYPE_NAMES[$item->type]) ?>
</td>
<!-- Step ID -->
<td class="d-none d-md-table-cell text-center">
<?php echo (int) $item->id; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
// Load the pagination
echo $this->pagination->getListFooter();
?>
<?php endif; ?>
<input type="hidden" name="task" value="">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</form>

View File

@ -0,0 +1,28 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Layout\LayoutHelper;
$displayData = [
'textPrefix' => 'COM_GUIDEDTOURS_STEPS',
'formURL' => 'index.php?option=com_guidedtours&view=steps',
'helpURL' => 'https://docs.joomla.org/Special:MyLanguage/Help4.x:Guided_Tours:_Steps',
'icon' => 'icon-map-signs',
];
$user = $this->getCurrentUser();
if ($user->authorise('core.create', 'com_guidedtours')) {
$displayData['createURL'] = 'index.php?option=com_guidedtours&task=step.add';
}
echo LayoutHelper::render('joomla.content.emptystate', $displayData);

View File

@ -0,0 +1,102 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
$app = Factory::getApplication();
$user = $app->getIdentity();
$input = $app->getInput();
$lang = $this->getLanguage()->getTag();
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate');
?>
<form action="<?php echo Route::_('index.php?option=com_guidedtours&view=tour&layout=edit&id=' .
(int) $this->item->id); ?>" method="post" name="adminForm" id="guidedtours-form" class="form-validate">
<div class="row title-alias form-vertical mb-3">
<div class="col-12 col-md-6">
<?php echo $this->form->renderField('title'); ?>
</div>
<div class="col-12 col-md-6">
<?php echo $this->form->renderField('uid'); ?>
</div>
</div>
<?php if ($this->item->id != 0 && strpos($this->item->title, 'GUIDEDTOUR') !== false) : ?>
<div class="row title-alias form-vertical mb-3">
<div class="col-12">
<?php $this->form->setFieldAttribute('title_translation', 'label', Text::sprintf('COM_GUIDEDTOURS_TITLE_TRANSLATION', $lang)); ?>
<?php echo $this->form->renderField('title_translation'); ?>
</div>
</div>
<?php endif; ?>
<div class="main-card">
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'details', 'recall' => true, 'breakpoint' => 768]); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'details', empty($this->item->id) ? Text::_('COM_GUIDEDTOURS_NEW_TOUR') : Text::_('COM_GUIDEDTOURS_EDIT_TOUR')); ?>
<div class="row">
<div class="col-lg-9">
<?php echo $this->form->renderField('url'); ?>
<?php echo $this->form->renderField('description'); ?>
<?php if ($this->item->id != 0 && strpos($this->item->description, 'GUIDEDTOUR') !== false) : ?>
<?php $this->form->setFieldAttribute('description_translation', 'label', Text::sprintf('COM_GUIDEDTOURS_DESCRIPTION_TRANSLATION', $lang)); ?>
<?php echo $this->form->renderField('description_translation'); ?>
<?php endif; ?>
</div>
<div class="col-lg-3">
<?php
// Set main fields.
$this->fields = [
'published',
'access',
'language',
'extensions',
'note',
];
echo LayoutHelper::render('joomla.edit.global', $this); ?>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'publishing', Text::_('JGLOBAL_FIELDSET_PUBLISHING')); ?>
<div class="row">
<div class="col-12 col-lg-8">
<fieldset id="fieldset-publishingdata" class="options-form">
<legend><?php echo Text::_('JGLOBAL_FIELDSET_PUBLISHING'); ?></legend>
<div>
<?php
$this->fields = [];
echo LayoutHelper::render('joomla.edit.publishingdata', $this); ?>
</div>
</fieldset>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
</div>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</form>

View File

@ -0,0 +1,259 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Factory;
use Joomla\CMS\HTML\Helpers\StringHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
use Joomla\Component\Guidedtours\Administrator\View\Tours\HtmlView;
/** @var HtmlView $this */
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('table.columns')
->useScript('multiselect');
try {
$app = Factory::getApplication();
} catch (Exception $e) {
die('Failed to get app');
}
$user = $app->getIdentity();
$userId = $user->get('id');
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
$saveOrder = $listOrder == 'a.ordering';
$section = null;
$mode = false;
$canEdit = $user->authorise('core.edit', 'com_guidedtours');
$canEditOwnTour = $user->authorise('core.edit.own', 'com_guidedtours');
$canEditStateTour = $user->authorise('core.edit.state', 'com_guidedtours');
$hasCheckinPermission = $user->authorise('core.manage', 'com_checkin');
if ($saveOrder && !empty($this->items)) {
$saveOrderingUrl =
'index.php?option=com_guidedtours&task=tours.saveOrderAjax&tmpl=component&'
. Session::getFormToken() . '=1';
HTMLHelper::_('draggablelist.draggable');
}
?>
<form action="<?php echo Route::_('index.php?option=com_guidedtours&view=tours'); ?>"
method="post" name="adminForm" id="adminForm">
<div id="j-main-container" class="j-main-container">
<?php
// Search tools bar
echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this]);
?>
<!-- If no tours -->
<?php if (empty($this->items)) :
?>
<!-- No tours -->
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span>
<span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('JGLOBAL_NO_MATCHING_RESULTS'); ?>
</div>
<?php endif; ?>
<!-- If there are tours, we start with the table -->
<?php if (!empty($this->items)) :
?>
<!-- Tours table starts here -->
<table class="table" id="toursList">
<caption class="visually-hidden">
<?php echo Text::_('COM_GUIDEDTOURS_GUIDEDTOURS_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<!-- Tours table header -->
<thead>
<tr>
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<!-- Ordering?-->
<th scope="col" class="w-3 text-center d-none d-md-table-cell">
<?php echo HTMLHelper::_(
'searchtools.sort',
'',
'a.ordering',
$listDirn,
$listOrder,
null,
'asc',
'JGRID_HEADING_ORDERING',
'icon-sort'
); ?>
</th>
<th scope="col" class="w-1 text-center">
<?php echo HTMLHelper::_(
'searchtools.sort',
'JSTATUS',
'a.published',
$listDirn,
$listOrder
); ?>
</th>
<th scope="col">
<?php echo Text::_('COM_GUIDEDTOURS_TITLE'); ?>
</th>
<th scope="col" class="d-none d-md-table-cell">
<?php echo Text::_('COM_GUIDEDTOURS_DESCRIPTION'); ?>
</th>
<th scope="col" class="w-10 text-center d-none d-md-table-cell">
<?php echo Text::_('COM_GUIDEDTOURS_STEPS'); ?>
</th>
<th scope="col" class="text-center w-10 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_ACCESS', 'a.access', $listDirn, $listOrder); ?>
</th>
<!-- Add language types if multi-language enabled -->
<?php if (Multilanguage::isEnabled()) : ?>
<th scope="col" class="text-center w-10 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_LANGUAGE', 'a.language', $this->state->get('list.direction'), $this->state->get('list.ordering')); ?>
</th>
<?php endif; ?>
<th scope="col" class="w-10 text-center d-none d-md-table-cell">
<?php echo HTMLHelper::_(
'searchtools.sort',
'JGRID_HEADING_ID',
'a.id',
$listDirn,
$listOrder
); ?>
</th>
</tr>
</thead>
<!-- Table body begins -->
<tbody <?php if ($saveOrder) : ?>
class="js-draggable" data-url="<?php echo $saveOrderingUrl; ?>" data-direction="<?php echo strtolower($listDirn); ?>" data-nested="true" <?php
endif; ?>>
<?php foreach ($this->items as $i => $item) :
$canEditOwn = $canEditOwnTour && $item->created_by == $userId;
$canCheckin = $hasCheckinPermission || $item->checked_out == $userId || is_null($item->checked_out);
$canChange = $canEditStateTour && $canCheckin;
?>
<!-- Row begins -->
<tr class="row<?php echo $i % 2; ?>" data-draggable-group="none">
<!-- Item Checkbox -->
<td class="text-center">
<?php echo HTMLHelper::_('grid.id', $i, $item->id, false, 'cid', 'cb', $item->title); ?>
</td>
<!-- Draggable handle -->
<td class="text-center d-none d-md-table-cell">
<?php
$iconClass = '';
if (!$canChange) {
$iconClass = ' inactive';
} elseif (!$saveOrder) {
$iconClass = ' inactive" title="' . Text::_('JORDERINGDISABLED');
}
?>
<span class="sortable-handler <?php echo $iconClass; ?>">
<span class="icon-ellipsis-v" aria-hidden="true"></span>
</span>
<?php if ($canChange && $saveOrder) : ?>
<input type="text" class="hidden text-area-order" name="order[]" size="5" value="<?php echo $item->ordering; ?>">
<?php endif; ?>
</td>
<!-- Item State -->
<td class="text-center">
<?php echo HTMLHelper::_(
'jgrid.published',
$item->published,
$i,
'tours.',
$canChange
); ?>
</td>
<th scope="row" class="has-context">
<div>
<?php if ($item->checked_out) : ?>
<?php echo HTMLHelper::_('jgrid.checkedout', $i, $item->editor, $item->checked_out_time, 'tours.', $canCheckin); ?>
<?php endif; ?>
<?php if ($canEdit || $canEditOwn) : ?>
<a href="<?php echo Route::_('index.php?option=com_guidedtours&task=tour.edit&id=' . $item->id); ?>" title="<?php echo Text::_('JACTION_EDIT'); ?> <?php echo $this->escape($item->title); ?>">
<?php echo $this->escape($item->title); ?>
</a>
<?php else : ?>
<?php echo $this->escape($item->title); ?>
<?php endif; ?>
<?php if ($item->note) : ?>
<div class="small break-word">
<?php echo Text::sprintf('JGLOBAL_LIST_NOTE', $this->escape($item->note)); ?>
</div>
<?php endif; ?>
</div>
</th>
<td class="d-none d-md-table-cell">
<?php echo StringHelper::truncate($item->description, 200, true, false); ?>
</td>
<td class="text-center btns d-none d-md-table-cell itemnumber">
<a class="btn btn-info" href="index.php?option=com_guidedtours&view=steps&tour_id=<?php echo $item->id; ?>">
<?php echo $item->steps_count; ?>
</a>
</td>
<!-- Adds access labels -->
<td class="small text-center d-none d-md-table-cell">
<?php echo $this->escape($item->access_level); ?>
</td>
<?php if (Multilanguage::isEnabled()) : ?>
<td class="text-center small d-none d-md-table-cell">
<?php echo LayoutHelper::render('joomla.content.language', $item); ?>
</td>
<?php endif; ?>
<!-- Tour ID -->
<td class="d-none d-md-table-cell text-center">
<?php echo (int) $item->id; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
// Load the pagination.
echo $this->pagination->getListFooter();
?>
<?php endif; ?>
<input type="hidden" name="task" value="">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</form>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<layout title="COM_GUIDEDTOURS_TOURS_VIEW_DEFAULT_TITLE">
<message>
<![CDATA[COM_GUIDEDTOURS_TOURS_VIEW_DEFAULT_DESC]]>
</message>
</layout>
</metadata>

View File

@ -0,0 +1,28 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_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\Layout\LayoutHelper;
$displayData = [
'textPrefix' => 'COM_GUIDEDTOURS_TOURS_LIST',
'formURL' => 'index.php?option=com_guidedtours&view=tours',
'helpURL' => 'https://docs.joomla.org/Special:MyLanguage/Help4.x:Guided_Tours',
'icon' => 'icon-map-signs',
];
$user = $this->getCurrentUser();
if ($user->authorise('core.create', 'com_guidedtours')) {
$displayData['createURL'] = 'index.php?option=com_guidedtours&task=tour.add';
}
echo LayoutHelper::render('joomla.content.emptystate', $displayData);