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,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<access component="com_templates">
<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" />
</section>
</access>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<config>
<help key="Template:_Options"/>
<inlinehelp button="show"/>
<fieldset
name="templates"
label="COM_TEMPLATES_SUBMENU_TEMPLATES"
description="COM_TEMPLATES_CONFIG_FIELDSET_DESC">
<field
name="template_positions_display"
type="radio"
label="COM_TEMPLATES_CONFIG_POSITIONS_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JDISABLED</option>
<option value="1">JENABLED</option>
</field>
<field
name="upload_limit"
type="number"
label="COM_TEMPLATES_CONFIG_UPLOAD_LABEL"
default="10"
extension="com_templates"
/>
<field
name="spacer"
type="spacer"
hr="true"
/>
<field
name="image_formats"
type="text"
label="COM_TEMPLATES_CONFIG_IMAGE_LABEL"
default="gif,bmp,jpg,jpeg,png,webp"
extension="com_templates"
/>
<field
name="source_formats"
type="text"
label="COM_TEMPLATES_CONFIG_SOURCE_LABEL"
default="txt,less,ini,xml,js,php,css,sass,scss,json"
extension="com_templates"
/>
<field
name="font_formats"
type="text"
label="COM_TEMPLATES_CONFIG_FONT_LABEL"
default="woff,woff2,ttf,otf"
extension="com_templates"
/>
<field
name="compressed_formats"
type="hidden"
default="zip"
extension="com_templates"
/>
<field
name="spacer2"
type="spacer"
hr="true"
/>
<field
name="difference"
type="radio"
label="COM_TEMPLATES_DIFF_OPTIONS"
layout="joomla.form.field.radio.switcher"
default="SideBySide"
>
<option value="Inline">COM_TEMPLATES_DIFF_OPTIONS_INLINE</option>
<option value="SideBySide">COM_TEMPLATES_DIFF_OPTIONS_SIDE</option>
</field>
</fieldset>
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
filter="rules"
validate="rules"
component="com_templates"
section="component"
/>
</fieldset>
</config>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<form addfieldprefix="Joomla\Component\Templates\Administrator\Field">
<field
name="client_id"
type="list"
label="JSITEADMIN"
filtermode="selector"
class="js-select-reset-on-change"
validate="options"
>
<option value="0">JSITE</option>
<option value="1">JADMINISTRATOR</option>
</field>
<fields name="filter">
<field
name="search"
type="text"
inputmode="search"
label="JSEARCH_FILTER"
description="COM_TEMPLATES_STYLES_FILTER_SEARCH_DESC"
hint="JSEARCH_FILTER"
noresults="COM_TEMPLATES_MSG_MANAGE_NO_STYLES"
/>
<field
name="menuitem"
type="menuitem"
label="COM_TEMPLATES_OPTION_SELECT_MENU_ITEM"
disable="separator,alias,heading,url"
showon="client_id:0"
class="js-select-submit-on-change"
>
<option value="">COM_TEMPLATES_OPTION_SELECT_MENU_ITEM</option>
<option value="-1">COM_TEMPLATES_OPTION_NONE</option>
</field>
<field
name="template"
type="templatename"
label="JOPTION_SELECT_TEMPLATE"
class="js-select-submit-on-change"
>
<option value="">JOPTION_SELECT_TEMPLATE</option>
</field>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
default="a.template ASC"
validate="options"
>
<option value="">JGLOBAL_SORT_BY</option>
<option value="a.title ASC">COM_TEMPLATES_HEADING_STYLE_ASC</option>
<option value="a.title DESC">COM_TEMPLATES_HEADING_STYLE_DESC</option>
<option value="a.home ASC">COM_TEMPLATES_HEADING_DEFAULT_ASC</option>
<option value="a.home DESC">COM_TEMPLATES_HEADING_DEFAULT_DESC</option>
<option value="a.template ASC">COM_TEMPLATES_HEADING_TEMPLATE_ASC</option>
<option value="a.template DESC">COM_TEMPLATES_HEADING_TEMPLATE_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,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<field
name="client_id"
type="list"
label="JSITEADMIN"
filtermode="selector"
class="js-select-reset-on-change"
validate="options"
>
<option value="0">JSITE</option>
<option value="1">JADMINISTRATOR</option>
</field>
<fields name="filter">
<field
name="search"
type="text"
inputmode="search"
label="JSEARCH_FILTER"
description="COM_TEMPLATES_TEMPLATES_FILTER_SEARCH_DESC"
hint="JSEARCH_FILTER"
noresults="COM_TEMPLATES_MSG_MANAGE_NO_TEMPLATES"
/>
</fields>
<fields name="list">
<field
name="fullordering"
type="list"
label="JGLOBAL_SORT_BY"
class="js-select-submit-on-change"
default="a.element ASC"
validate="options"
>
<option value="">JGLOBAL_SORT_BY</option>
<option value="a.element ASC">COM_TEMPLATES_HEADING_TEMPLATE_ASC</option>
<option value="a.element DESC">COM_TEMPLATES_HEADING_TEMPLATE_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,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset>
<field
name="extension_id"
type="hidden"
/>
<field
name="filename"
type="hidden"
/>
<field
name="source"
type="editor"
label="COM_TEMPLATES_FIELD_SOURCE_LABEL"
editor="codemirror|none"
buttons="no"
rows="40"
cols="80"
syntax="php"
filter="raw"
/>
<field
name="core"
type="editor"
label="COM_TEMPLATES_FIELD_SOURCE_LABEL"
editor="codemirror|none"
buttons="no"
rows="40"
cols="80"
syntax="php"
filter="raw"
readonly="true"
/>
<field
name="show_core"
type="radio"
label="COM_TEMPLATES_LAYOUTS_DIFFVIEW_CORE"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
<field
name="show_diff"
type="radio"
label="COM_TEMPLATES_LAYOUTS_DIFFVIEW_DIFF"
layout="joomla.form.field.radio.switcher"
default="0"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</form>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset>
<field
name="id"
type="number"
label="JGLOBAL_FIELD_ID_LABEL"
default="0"
readonly="true"
class="readonly"
/>
<field
name="template"
type="text"
label="COM_TEMPLATES_FIELD_TEMPLATE_LABEL"
class="readonly"
readonly="true"
/>
<field
name="client_id"
type="hidden"
label="COM_TEMPLATES_FIELD_CLIENT_LABEL"
class="readonly"
default="0"
readonly="true"
/>
<field
name="title"
type="text"
label="COM_TEMPLATES_FIELD_TITLE_LABEL"
required="true"
/>
<field
name="assigned"
type="hidden"
/>
<field
name="inheritable"
type="hidden"
readonly="true"
/>
<field
name="parent"
type="hidden"
readonly="true"
/>
</fieldset>
</form>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset>
<field
name="home"
type="radio"
label="COM_TEMPLATES_FIELD_HOME_LABEL"
layout="joomla.form.field.radio.switcher"
default="0"
validate="options"
>
<option value="0">JNO</option>
<option value="1">JYES</option>
</field>
</fieldset>
</form>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<form>
<fieldset>
<field
name="home"
type="contentlanguage"
label="COM_TEMPLATES_FIELD_HOME_LABEL"
default="0"
validate="options"
>
<option value="0">JNO</option>
<option value="1">JALL</option>
</field>
</fieldset>
</form>

View File

@ -0,0 +1,27 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
*/
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template Helper class.
*
* @since 3.2
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Templates\Administrator\Helper\TemplateHelper instead
*/
abstract class TemplateHelper extends \Joomla\Component\Templates\Administrator\Helper\TemplateHelper
{
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*
* @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
*/
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Templates component helper.
*
* @since 1.6
*
* @deprecated 4.3 will be removed in 6.0
* Use \Joomla\Component\Templates\Administrator\Helper\TemplatesHelper instead
*/
class TemplatesHelper extends \Joomla\Component\Templates\Administrator\Helper\TemplatesHelper
{
}

View File

@ -0,0 +1,55 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2018 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\Templates\Administrator\Extension\TemplatesComponent;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
/**
* The templates service provider.
*
* @since 4.0.0
*/
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.0.0
*/
public function register(Container $container)
{
$container->registerServiceProvider(new MVCFactory('\\Joomla\\Component\\Templates'));
$container->registerServiceProvider(new ComponentDispatcherFactory('\\Joomla\\Component\\Templates'));
$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new TemplatesComponent($container->get(ComponentDispatcherFactoryInterface::class));
$component->setMVCFactory($container->get(MVCFactoryInterface::class));
$component->setRegistry($container->get(Registry::class));
return $component;
}
);
}
};

View File

@ -0,0 +1,70 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\Router\Route;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Templates manager display controller.
*
* @since 1.6
*/
class DisplayController extends BaseController
{
/**
* @var string The default view.
* @since 1.6
*/
protected $default_view = 'styles';
/**
* Method to display a view.
*
* @param boolean $cachable If true, the view output will be cached
* @param boolean $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 or false on failure.
*
* @since 1.5
*/
public function display($cachable = false, $urlparams = false)
{
$view = $this->input->get('view', 'styles');
$layout = $this->input->get('layout', 'default');
$id = $this->input->getInt('id');
// For JSON requests
if ($this->app->getDocument()->getType() == 'json') {
return parent::display();
}
// Check for edit form.
if ($view == 'style' && $layout == 'edit' && !$this->checkEditId('com_templates.edit.style', $id)) {
// Somehow the person just went to the form - we don't allow that.
if (!\count($this->app->getMessageQueue())) {
$this->setMessage(Text::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $id), 'error');
}
$this->setRedirect(Route::_('index.php?option=com_templates&view=styles', false));
return false;
}
return parent::display();
}
}

View File

@ -0,0 +1,148 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Controller;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\FormController;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template style controller class.
*
* @since 1.6
*/
class StyleController extends FormController
{
/**
* The prefix to use with controller messages.
*
* @var string
* @since 1.6
*/
protected $text_prefix = 'COM_TEMPLATES_STYLE';
/**
* Method to save a template style.
*
* @param string $key The name of the primary key of the URL variable.
* @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions).
*
* @return boolean True if successful, false otherwise.
*
* @since 1.6
*/
public function save($key = null, $urlVar = null)
{
$this->checkToken();
if ($this->app->getDocument()->getType() === 'json') {
$model = $this->getModel('Style', 'Administrator');
$table = $model->getTable();
$data = $this->input->post->get('params', [], 'array');
$checkin = $table->hasField('checked_out');
$context = $this->option . '.edit.' . $this->context;
$item = $model->getItem($this->app->getTemplate(true)->id);
// Setting received params
$item->set('params', $data);
$data = $item->getProperties();
unset($data['xml']);
$key = $table->getKeyName();
// Access check.
if (!$this->allowSave($data, $key)) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return false;
}
Form::addFormPath(JPATH_ADMINISTRATOR . '/components/com_templates/forms');
// Validate the posted data.
// Sometimes the form needs some posted data, such as for plugins and modules.
$form = $model->getForm($data, false);
if (!$form) {
$this->app->enqueueMessage($model->getError(), 'error');
return false;
}
// Test whether the data is valid.
$validData = $model->validate($form, $data);
if ($validData === false) {
// Get the validation messages.
$errors = $model->getErrors();
// Push up to three validation messages out to the user.
for ($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) {
if ($errors[$i] instanceof \Exception) {
$this->app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$this->app->enqueueMessage($errors[$i], 'warning');
}
}
// Save the data in the session.
$this->app->setUserState($context . '.data', $data);
return false;
}
if (!isset($validData['tags'])) {
$validData['tags'] = null;
}
// Attempt to save the data.
if (!$model->save($validData)) {
// Save the data in the session.
$this->app->setUserState($context . '.data', $validData);
$this->app->enqueueMessage(Text::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()), 'error');
return false;
}
// Save succeeded, so check-in the record.
if ($checkin && $model->checkin($validData[$key]) === false) {
// Save the data in the session.
$this->app->setUserState($context . '.data', $validData);
// Check-in failed, so go back to the record and display a notice.
$this->app->enqueueMessage(Text::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()), 'error');
return false;
}
// Redirect the user and adjust session state
// Set the record data in the session.
$recordId = $model->getState($this->context . '.id');
$this->holdEditId($context, $recordId);
$this->app->setUserState($context . '.data', null);
$model->checkout($recordId);
// Invoke the postSave method to allow for the child class to access the model.
$this->postSaveHook($model, $validData);
return true;
}
return parent::save($key, $urlVar);
}
}

View File

@ -0,0 +1,145 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Controller;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\AdminController;
use Joomla\CMS\MVC\Model\BaseDatabaseModel;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template styles list controller class.
*
* @since 1.6
*/
class StylesController extends AdminController
{
/**
* Method to clone and existing template style.
*
* @return void
*/
public function duplicate()
{
// Check for request forgeries
$this->checkToken();
$pks = (array) $this->input->post->get('cid', [], 'int');
// Remove zero values resulting from input filter
$pks = array_filter($pks);
try {
if (empty($pks)) {
throw new \Exception(Text::_('COM_TEMPLATES_NO_TEMPLATE_SELECTED'));
}
$model = $this->getModel();
$model->duplicate($pks);
$this->setMessage(Text::_('COM_TEMPLATES_SUCCESS_DUPLICATED'));
} catch (\Exception $e) {
$this->app->enqueueMessage($e->getMessage(), 'error');
}
$this->setRedirect('index.php?option=com_templates&view=styles');
}
/**
* Proxy for getModel.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return BaseDatabaseModel
*
* @since 1.6
*/
public function getModel($name = 'Style', $prefix = 'Administrator', $config = [])
{
return parent::getModel($name, $prefix, ['ignore_request' => true]);
}
/**
* Method to set the home template for a client.
*
* @return void
*
* @since 1.6
*/
public function setDefault()
{
// Check for request forgeries
$this->checkToken();
$pks = (array) $this->input->post->get('cid', [], 'int');
// Remove zero values resulting from input filter
$pks = array_filter($pks);
try {
if (empty($pks)) {
throw new \Exception(Text::_('COM_TEMPLATES_NO_TEMPLATE_SELECTED'));
}
// Pop off the first element.
$id = array_shift($pks);
/** @var \Joomla\Component\Templates\Administrator\Model\StyleModel $model */
$model = $this->getModel();
$model->setHome($id);
$this->setMessage(Text::_('COM_TEMPLATES_SUCCESS_HOME_SET'));
} catch (\Exception $e) {
$this->setMessage($e->getMessage(), 'warning');
}
$this->setRedirect('index.php?option=com_templates&view=styles');
}
/**
* Method to unset the default template for a client and for a language
*
* @return void
*
* @since 1.6
*/
public function unsetDefault()
{
// Check for request forgeries
$this->checkToken('request');
$pks = (array) $this->input->get->get('cid', [], 'int');
// Remove zero values resulting from input filter
$pks = array_filter($pks);
try {
if (empty($pks)) {
throw new \Exception(Text::_('COM_TEMPLATES_NO_TEMPLATE_SELECTED'));
}
// Pop off the first element.
$id = array_shift($pks);
/** @var \Joomla\Component\Templates\Administrator\Model\StyleModel $model */
$model = $this->getModel();
$model->unsetHome($id);
$this->setMessage(Text::_('COM_TEMPLATES_SUCCESS_HOME_UNSET'));
} catch (\Exception $e) {
$this->setMessage($e->getMessage(), 'warning');
}
$this->setRedirect('index.php?option=com_templates&view=styles');
}
}

View File

@ -0,0 +1,999 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Controller;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Controller\BaseController;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Session\Session;
use Joomla\Filesystem\Path;
use Joomla\Input\Input;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template style controller class.
*
* @since 1.6
*/
class TemplateController extends BaseController
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param MVCFactoryInterface $factory The factory.
* @param CMSApplication $app The Application for the dispatcher
* @param Input $input Input
*
* @since 1.6
* @see BaseController
*/
public function __construct($config = [], MVCFactoryInterface $factory = null, $app = null, $input = null)
{
parent::__construct($config, $factory, $app, $input);
$this->registerTask('apply', 'save');
$this->registerTask('unpublish', 'publish');
$this->registerTask('publish', 'publish');
$this->registerTask('deleteOverrideHistory', 'publish');
}
/**
* Method for closing the template.
*
* @return void
*
* @since 3.2
*/
public function cancel()
{
$this->setRedirect(Route::_('index.php?option=com_templates&view=templates', false));
}
/**
* Method for closing a file.
*
* @return void
*
* @since 3.2
*/
public function close()
{
$file = base64_encode('home');
$id = (int) $this->input->get('id', 0, 'int');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' .
$file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
/**
* Marked as Checked/Unchecked of override history.
*
* @return void
*
* @since 4.0.0
*/
public function publish()
{
// Check for request forgeries.
$this->checkToken();
$file = $this->input->get('file');
$id = $this->input->get('id');
$ids = (array) $this->input->get('cid', [], 'string');
$values = ['publish' => 1, 'unpublish' => 0, 'deleteOverrideHistory' => -3];
$task = $this->getTask();
$value = ArrayHelper::getValue($values, $task, 0, 'int');
if (empty($ids)) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_NO_FILE_SELECTED'), 'warning');
} else {
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
// Change the state of the records.
if (!$model->publish($ids, $value, $id)) {
$this->setMessage(implode('<br>', $model->getErrors()), 'warning');
} else {
if ($value === 1) {
$ntext = 'COM_TEMPLATES_N_OVERRIDE_CHECKED';
} elseif ($value === 0) {
$ntext = 'COM_TEMPLATES_N_OVERRIDE_UNCHECKED';
} elseif ($value === -3) {
$ntext = 'COM_TEMPLATES_N_OVERRIDE_DELETED';
}
$this->setMessage(Text::plural($ntext, \count($ids)));
}
}
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' .
$file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
/**
* Method for copying the template.
*
* @return boolean true on success, false otherwise
*
* @since 3.2
*/
public function copy()
{
// Check for request forgeries
$this->checkToken();
$app = $this->app;
$this->input->set('installtype', 'folder');
$newNameRaw = $this->input->get('new_name', null, 'string');
// Only accept letters, numbers and underscore for template name
$newName = preg_replace('/[^a-zA-Z0-9_]/', '', $newNameRaw);
$templateID = (int) $this->input->getInt('id', 0);
$file = (string) $this->input->get('file', '', 'cmd');
// Access check.
if (!$this->allowEdit()) {
$app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return false;
}
$this->setRedirect('index.php?option=com_templates&view=template&id=' . $templateID . '&file=' . $file);
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel('Template', 'Administrator');
$model->setState('new_name', $newName);
$model->setState('tmp_prefix', uniqid('template_copy_'));
$model->setState('to_path', $app->get('tmp_path') . '/' . $model->getState('tmp_prefix'));
// Process only if we have a new name entered
if (\strlen($newName) > 0) {
if (!$this->app->getIdentity()->authorise('core.create', 'com_templates')) {
// User is not authorised to delete
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_CREATE_NOT_PERMITTED'), 'error');
return false;
}
// Check that new name is valid
if (($newNameRaw !== null) && ($newName !== $newNameRaw)) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_INVALID_TEMPLATE_NAME'), 'error');
return false;
}
// Check that new name doesn't already exist
if (!$model->checkNewName()) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_DUPLICATE_TEMPLATE_NAME'), 'error');
return false;
}
// Check that from name does exist and get the folder name
$fromName = $model->getFromName();
if (!$fromName) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_INVALID_FROM_NAME'), 'error');
return false;
}
// Call model's copy method
if (!$model->copy()) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_COULD_NOT_COPY'), 'error');
return false;
}
// Call installation model
$this->input->set('install_directory', $app->get('tmp_path') . '/' . $model->getState('tmp_prefix'));
/** @var \Joomla\Component\Installer\Administrator\Model\InstallModel $installModel */
$installModel = $this->app->bootComponent('com_installer')
->getMVCFactory()->createModel('Install', 'Administrator');
$this->app->getLanguage()->load('com_installer');
if (!$installModel->install()) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_COULD_NOT_INSTALL'), 'error');
return false;
}
$this->setMessage(Text::sprintf('COM_TEMPLATES_COPY_SUCCESS', $newName));
$model->cleanup();
return true;
}
$this->setMessage(Text::sprintf('COM_TEMPLATES_ERROR_INVALID_TEMPLATE_NAME'), 'error');
return false;
}
/**
* Method to get a model object, loading it if required.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional (note, the empty array is atypical compared to other models).
*
* @return \Joomla\CMS\MVC\Model\BaseDatabaseModel The model.
*
* @since 3.2
*/
public function getModel($name = 'Template', $prefix = 'Administrator', $config = [])
{
return parent::getModel($name, $prefix, $config);
}
/**
* Method to check if you can add a new record.
*
* @return boolean
*
* @since 3.2
*/
protected function allowEdit()
{
return $this->app->getIdentity()->authorise('core.admin');
}
/**
* Saves a template source file.
*
* @return void
*
* @since 3.2
*/
public function save()
{
// Check for request forgeries.
$this->checkToken();
$data = $this->input->post->get('jform', [], 'array');
$task = $this->getTask();
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$fileName = (string) $this->input->getCmd('file', '');
$explodeArray = explode(':', str_replace('//', '/', base64_decode($fileName)));
// Access check.
if (!$this->allowEdit()) {
$this->setMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
// Match the stored id's with the submitted.
if (empty($data['extension_id']) || empty($data['filename'])) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_SOURCE_ID_FILENAME_MISMATCH'), 'error');
return;
}
if ($data['extension_id'] != $model->getState('extension.id')) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_SOURCE_ID_FILENAME_MISMATCH'), 'error');
return;
} elseif (str_ends_with(end($explodeArray), Path::clean($data['filename'], '/'))) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_SOURCE_ID_FILENAME_MISMATCH'), 'error');
return;
}
// Validate the posted data.
$form = $model->getForm();
if (!$form) {
$this->setMessage($model->getError(), 'error');
return;
}
$data = $model->validate($form, $data);
// Check for validation errors.
if ($data === false) {
// Get the validation messages.
$errors = $model->getErrors();
// Push up to three validation messages out to the user.
for ($i = 0, $n = \count($errors); $i < $n && $i < 3; $i++) {
if ($errors[$i] instanceof \Exception) {
$this->app->enqueueMessage($errors[$i]->getMessage(), 'warning');
} else {
$this->app->enqueueMessage($errors[$i], 'warning');
}
}
// Redirect back to the edit screen.
$url = 'index.php?option=com_templates&view=template&id=' . $model->getState('extension.id') . '&file=' . $fileName . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
return;
}
// Attempt to save the data.
if (!$model->save($data)) {
// Redirect back to the edit screen.
$this->setMessage(Text::sprintf('JERROR_SAVE_FAILED', $model->getError()), 'warning');
$url = 'index.php?option=com_templates&view=template&id=' . $model->getState('extension.id') . '&file=' . $fileName . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
return;
}
$this->setMessage(Text::_('COM_TEMPLATES_FILE_SAVE_SUCCESS'));
// Redirect the user based on the chosen task.
switch ($task) {
case 'apply':
// Redirect back to the edit screen.
$url = 'index.php?option=com_templates&view=template&id=' . $model->getState('extension.id') . '&file=' . $fileName . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
break;
default:
// Redirect to the list screen.
$file = base64_encode('home');
$id = (int) $this->input->get('id', 0, 'int');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
break;
}
}
/**
* Method for creating override.
*
* @return void
*
* @since 3.2
*/
public function overrides()
{
// Check for request forgeries.
$this->checkToken('get');
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$file = (string) $this->input->getCmd('file', '');
$override = (string) InputFilter::getInstance(
[],
[],
InputFilter::ONLY_BLOCK_DEFINED_TAGS,
InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
)
->clean(base64_decode($this->input->getBase64('folder', '')), 'path');
$id = (int) $this->input->get('id', 0, 'int');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
$model->createOverride($override);
// Redirect back to the edit screen.
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
/**
* Method for deleting a file.
*
* @return void
*
* @since 3.2
*/
public function delete()
{
// Check for request forgeries
$this->checkToken();
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if (base64_decode(urldecode($file)) == '/index.php') {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_INDEX_DELETE'), 'warning');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif (base64_decode(urldecode($file)) == '/joomla.asset.json') {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_ASSET_FILE_DELETE'), 'warning');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif ($model->deleteFile($file)) {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_DELETE_SUCCESS'));
$file = base64_encode('home');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_FILE_DELETE'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for creating a new file.
*
* @return void
*
* @since 3.2
*/
public function createFile()
{
// Check for request forgeries
$this->checkToken();
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->get('file', '', 'cmd');
$name = (string) $this->input->get('name', '', 'cmd');
$location = (string) InputFilter::getInstance(
[],
[],
InputFilter::ONLY_BLOCK_DEFINED_TAGS,
InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
)
->clean(base64_decode($this->input->getBase64('address', '')), 'path');
$type = (string) $this->input->get('type', '', 'cmd');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if ($type == 'null') {
$this->setMessage(Text::_('COM_TEMPLATES_INVALID_FILE_TYPE'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif (!preg_match('/^(?!\.)(?!.*\.$)(?!.*\.\.)[a-zA-Z0-9_.-]+$/', $name)) {
$this->setMessage(Text::_('COM_TEMPLATES_INVALID_FILE_NAME'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif ($model->createFile($name, $type, $location)) {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_CREATE_SUCCESS'));
$file = urlencode(base64_encode($location . '/' . $name . '.' . $type));
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_FILE_CREATE'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for uploading a file.
*
* @return void
*
* @since 3.2
*/
public function uploadFile()
{
// Check for request forgeries
$this->checkToken();
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
$upload = $this->input->files->get('files');
$location = (string) InputFilter::getInstance(
[],
[],
InputFilter::ONLY_BLOCK_DEFINED_TAGS,
InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
)
->clean(base64_decode($this->input->getBase64('address', '')), 'path');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if ($return = $model->uploadFile($upload, $location)) {
$this->setMessage(Text::sprintf('COM_TEMPLATES_FILE_UPLOAD_SUCCESS', $upload['name']));
$redirect = base64_encode($return);
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $redirect . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_FILE_UPLOAD'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for creating a new folder.
*
* @return void
*
* @since 3.2
*/
public function createFolder()
{
// Check for request forgeries
$this->checkToken();
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
$name = $this->input->get('name');
$location = (string) InputFilter::getInstance(
[],
[],
InputFilter::ONLY_BLOCK_DEFINED_TAGS,
InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
)
->clean(base64_decode($this->input->getBase64('address', '')), 'path');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if (!preg_match('/^[a-zA-Z0-9-_.]+$/', $name)) {
$this->setMessage(Text::_('COM_TEMPLATES_INVALID_FOLDER_NAME'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif ($model->createFolder($name, $location)) {
$this->setMessage(Text::_('COM_TEMPLATES_FOLDER_CREATE_SUCCESS'));
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_FOLDER_CREATE'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for deleting a folder.
*
* @return void
*
* @since 3.2
*/
public function deleteFolder()
{
// Check for request forgeries
$this->checkToken();
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$id = (int) $this->input->get('id', 0, 'int');
$isMedia = (int) $this->input->get('isMedia', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
$location = (string) InputFilter::getInstance(
[],
[],
InputFilter::ONLY_BLOCK_DEFINED_TAGS,
InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
)
->clean(base64_decode($this->input->getBase64('address', '')), 'path');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if (empty($location)) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_ROOT_DELETE'), 'warning');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
} elseif ($model->deleteFolder($location)) {
$this->setMessage(Text::_('COM_TEMPLATES_FOLDER_DELETE_SUCCESS'));
if (stristr(base64_decode($file), $location) != false) {
$file = base64_encode('home');
}
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_FOLDER_DELETE_ERROR'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for renaming a file.
*
* @return void
*
* @since 3.2
*/
public function renameFile()
{
// Check for request forgeries
$this->checkToken();
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$id = (int) $this->input->get('id', 0, 'int');
$isMedia = (int) $this->input->get('isMedia', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
$newName = $this->input->get('new_name');
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if (base64_decode(urldecode($file)) == '/index.php') {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_RENAME_INDEX'), 'warning');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
} elseif (base64_decode(urldecode($file)) == '/joomla.asset.json') {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_RENAME_ASSET_FILE'), 'warning');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
} elseif (!preg_match('/^(?!\.)(?!.*\.$)(?!.*\.\.)[a-zA-Z0-9_.-]+$/', $newName)) {
$this->setMessage(Text::_('COM_TEMPLATES_INVALID_FILE_NAME'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
} elseif ($rename = $model->renameFile($file, $newName)) {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_RENAME_SUCCESS'));
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $rename . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_FILE_RENAME'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $isMedia;
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for cropping an image.
*
* @return void
*
* @since 3.2
*/
public function cropImage()
{
// Check for request forgeries
$this->checkToken();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->get('file', '', 'cmd');
$x = $this->input->get('x');
$y = $this->input->get('y');
$w = $this->input->get('w');
$h = $this->input->get('h');
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if (empty($w) && empty($h) && empty($x) && empty($y)) {
$this->setMessage(Text::_('COM_TEMPLATES_CROP_AREA_ERROR'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif ($model->cropImage($file, $w, $h, $x, $y)) {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_CROP_SUCCESS'));
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_CROP_ERROR'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for resizing an image.
*
* @return void
*
* @since 3.2
*/
public function resizeImage()
{
// Check for request forgeries
$this->checkToken();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
$width = $this->input->get('width');
$height = $this->input->get('height');
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if ($model->resizeImage($file, $width, $height)) {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_RESIZE_SUCCESS'));
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_RESIZE_ERROR'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for copying a file.
*
* @return void
*
* @since 3.2
*/
public function copyFile()
{
// Check for request forgeries
$this->checkToken();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
$newName = $this->input->get('new_name');
$location = (string) InputFilter::getInstance(
[],
[],
InputFilter::ONLY_BLOCK_DEFINED_TAGS,
InputFilter::ONLY_BLOCK_DEFINED_ATTRIBUTES
)
->clean(base64_decode($this->input->getBase64('address', '')), 'path');
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if (!preg_match('/^[a-zA-Z0-9-_]+$/', $newName)) {
$this->setMessage(Text::_('COM_TEMPLATES_INVALID_FILE_NAME'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} elseif ($model->copyFile($newName, $location, $file)) {
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_COPY_FAIL'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file . '&isMedia=' . $this->input->getInt('isMedia', 0);
$this->setRedirect(Route::_($url, false));
}
}
/**
* Method for extracting an archive file.
*
* @return void
*
* @since 3.2
*/
public function extractArchive()
{
// Check for request forgeries
$this->checkToken();
$id = (int) $this->input->get('id', 0, 'int');
$file = (string) $this->input->getCmd('file', '');
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return;
}
if ($model->extractArchive($file)) {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_ARCHIVE_EXTRACT_SUCCESS'));
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file;
$this->setRedirect(Route::_($url, false));
} else {
$this->setMessage(Text::_('COM_TEMPLATES_FILE_ARCHIVE_EXTRACT_FAIL'), 'error');
$url = 'index.php?option=com_templates&view=template&id=' . $id . '&file=' . $file;
$this->setRedirect(Route::_($url, false));
}
}
/**
* Fetch and report updates in \JSON format, for AJAX requests
*
* @return void
*
* @since 4.0.0
*/
public function ajax()
{
$app = $this->app;
if (!Session::checkToken('get')) {
$app->setHeader('status', 403, true);
$app->sendHeaders();
echo Text::_('JINVALID_TOKEN_NOTICE');
$app->close();
}
// Checks status of installer override plugin.
if (!PluginHelper::isEnabled('installer', 'override')) {
$error = ['installerOverride' => 'disabled'];
echo json_encode($error);
$app->close();
}
/** @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel();
$result = $model->getUpdatedList(true, true);
echo json_encode($result);
$app->close();
}
/**
* Method for creating a child template.
*
* @return boolean true on success, false otherwise
*
* @since 4.1.0
*/
public function child()
{
// Check for request forgeries
$this->checkToken();
// Access check.
if (!$this->allowEdit()) {
$this->app->enqueueMessage(Text::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'), 'error');
return false;
}
$this->input->set('installtype', 'folder');
$newNameRaw = $this->input->get('new_name', null, 'string');
// Only accept letters, numbers and underscore for template name
$newName = preg_replace('/[^a-zA-Z0-9_]/', '', $newNameRaw);
$templateID = (int) $this->input->getInt('id', 0);
$file = (string) $this->input->get('file', '', 'cmd');
$extraStyles = (array) $this->input->get('style_ids', [], 'array');
$this->setRedirect('index.php?option=com_templates&view=template&id=' . $templateID . '&file=' . $file);
/* @var \Joomla\Component\Templates\Administrator\Model\TemplateModel $model */
$model = $this->getModel('Template', 'Administrator');
$model->setState('new_name', $newName);
$model->setState('tmp_prefix', uniqid('template_child_'));
$model->setState('to_path', $this->app->get('tmp_path') . '/' . $model->getState('tmp_prefix'));
// Process only if we have a new name entered
if (!\strlen($newName)) {
$this->setMessage(Text::sprintf('COM_TEMPLATES_ERROR_INVALID_TEMPLATE_NAME'), 'error');
return false;
}
// Process only if user is allowed to create child template
if (!$this->app->getIdentity()->authorise('core.create', 'com_templates')) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_CREATE_NOT_PERMITTED'), 'error');
return false;
}
// Check that new name is valid
if (($newNameRaw !== null) && ($newName !== $newNameRaw)) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_INVALID_TEMPLATE_NAME'), 'error');
return false;
}
// Check that new name doesn't already exist
if (!$model->checkNewName()) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_DUPLICATE_TEMPLATE_NAME'), 'error');
return false;
}
// Check that from name does exist and get the folder name
$fromName = $model->getFromName();
if (!$fromName) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_INVALID_FROM_NAME'), 'error');
return false;
}
// Call model's copy method
if (!$model->child()) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_COULD_NOT_COPY'), 'error');
return false;
}
// Call installation model
$this->input->set('install_directory', $this->app->get('tmp_path') . '/' . $model->getState('tmp_prefix'));
/** @var \Joomla\Component\Installer\Administrator\Model\InstallModel $installModel */
$installModel = $this->app->bootComponent('com_installer')
->getMVCFactory()->createModel('Install', 'Administrator');
$this->app->getLanguage()->load('com_installer');
if (!$installModel->install()) {
$this->setMessage(Text::_('COM_TEMPLATES_ERROR_COULD_NOT_INSTALL'), 'error');
return false;
}
$this->setMessage(Text::sprintf('COM_TEMPLATES_CHILD_SUCCESS', $newName));
$model->cleanup();
if (\count($extraStyles) > 0) {
$model->setState('stylesToCopy', $extraStyles);
$model->copyStyles();
}
return true;
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Extension;
use Joomla\CMS\Extension\BootableExtensionInterface;
use Joomla\CMS\Extension\MVCComponent;
use Joomla\CMS\HTML\HTMLRegistryAwareTrait;
use Joomla\Component\Templates\Administrator\Service\HTML\Templates;
use Psr\Container\ContainerInterface;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Component class for com_templates
*
* @since 4.0.0
*/
class TemplatesComponent extends MVCComponent implements BootableExtensionInterface
{
use HTMLRegistryAwareTrait;
/**
* 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.0.0
*/
public function boot(ContainerInterface $container)
{
$this->getRegistry()->register('templates', new Templates());
}
}

View File

@ -0,0 +1,48 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Field;
use Joomla\CMS\Form\Field\ListField;
use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template Location field.
*
* @since 3.5
*/
class TemplatelocationField extends ListField
{
/**
* The form field type.
*
* @var string
* @since 3.5
*/
protected $type = 'TemplateLocation';
/**
* Method to get the field options.
*
* @return array The field option objects.
*
* @since 3.5
*/
public function getOptions()
{
$options = TemplatesHelper::getClientOptions();
return array_merge(parent::getOptions(), $options);
}
}

View File

@ -0,0 +1,54 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2015 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Field;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Field\ListField;
use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template Name field.
*
* @since 3.5
*/
class TemplatenameField extends ListField
{
/**
* The form field type.
*
* @var string
* @since 3.5
*/
protected $type = 'TemplateName';
/**
* Method to get the field options.
*
* @return array The field option objects.
*
* @since 1.6
*/
public function getOptions()
{
// Get the client_id filter from the user state.
$clientId = Factory::getApplication()->getUserStateFromRequest('com_templates.styles.client_id', 'client_id', '0', 'string');
// Get the templates for the selected client_id.
$options = TemplatesHelper::getTemplateOptions($clientId);
// Merge into the parent options.
return array_merge(parent::getOptions(), $options);
}
}

View File

@ -0,0 +1,170 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Helper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Language\Text;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template Helper class.
*
* @since 3.2
*/
abstract class TemplateHelper
{
/**
* Checks if the file is an image
*
* @param string $fileName The filename
*
* @return boolean
*
* @since 3.2
*/
public static function getTypeIcon($fileName)
{
// Get file extension
return strtolower(substr($fileName, strrpos($fileName, '.') + 1));
}
/**
* Checks if the file can be uploaded
*
* @param array $file File information
* @param string $err An error message to be returned
*
* @return boolean
*
* @since 3.2
*/
public static function canUpload($file, $err = '')
{
$params = ComponentHelper::getParams('com_templates');
if (empty($file['name'])) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_UPLOAD_INPUT'), 'error');
return false;
}
// Media file names should never have executable extensions buried in them.
$executable = [
'exe', 'phtml','java', 'perl', 'py', 'asp','dll', 'go', 'jar',
'ade', 'adp', 'bat', 'chm', 'cmd', 'com', 'cpl', 'hta', 'ins', 'isp',
'jse', 'lib', 'mde', 'msc', 'msp', 'mst', 'pif', 'scr', 'sct', 'shb',
'sys', 'vb', 'vbe', 'vbs', 'vxd', 'wsc', 'wsf', 'wsh',
];
$explodedFileName = explode('.', $file['name']);
if (\count($explodedFileName) > 2) {
foreach ($executable as $extensionName) {
if (\in_array($extensionName, $explodedFileName)) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_EXECUTABLE'), 'error');
return false;
}
}
}
if ($file['name'] !== File::makeSafe($file['name']) || preg_match('/\s/', File::makeSafe($file['name']))) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_WARNFILENAME'), 'error');
return false;
}
$format = strtolower(File::getExt($file['name']));
$imageTypes = explode(',', $params->get('image_formats', 'gif,bmp,jpg,jpeg,png,webp'));
$sourceTypes = explode(',', $params->get('source_formats', 'txt,less,ini,xml,js,php,css,scss,sass,json'));
$fontTypes = explode(',', $params->get('font_formats', 'woff,woff2,ttf,otf'));
$archiveTypes = explode(',', $params->get('compressed_formats', 'zip'));
$allowable = array_merge($imageTypes, $sourceTypes, $fontTypes, $archiveTypes);
if ($format == '' || $format == false || (!\in_array($format, $allowable))) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_WARNFILETYPE'), 'error');
return false;
}
if (\in_array($format, $archiveTypes)) {
$zip = new \ZipArchive();
if ($zip->open($file['tmp_name']) === true) {
for ($i = 0; $i < $zip->numFiles; $i++) {
$entry = $zip->getNameIndex($i);
$endString = substr($entry, -1);
if ($endString != DIRECTORY_SEPARATOR) {
$explodeArray = explode('.', $entry);
$ext = end($explodeArray);
if (!\in_array($ext, $allowable)) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_FILE_UNSUPPORTED_ARCHIVE'), 'error');
return false;
}
}
}
} else {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_FILE_ARCHIVE_OPEN_FAIL'), 'error');
return false;
}
}
// Max upload size set to 10 MB for Template Manager
$maxSize = (int) ($params->get('upload_limit') * 1024 * 1024);
if ($maxSize > 0 && (int) $file['size'] > $maxSize) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_WARNFILETOOLARGE'), 'error');
return false;
}
$xss_check = file_get_contents($file['tmp_name'], false, null, -1, 256);
$html_tags = [
'abbr', 'acronym', 'address', 'applet', 'area', 'audioscope', 'base', 'basefont', 'bdo', 'bgsound', 'big', 'blackface', 'blink', 'blockquote',
'body', 'bq', 'br', 'button', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'comment', 'custom', 'dd', 'del', 'dfn', 'dir', 'div',
'dl', 'dt', 'em', 'embed', 'fieldset', 'fn', 'font', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'hr', 'html',
'iframe', 'ilayer', 'img', 'input', 'ins', 'isindex', 'keygen', 'kbd', 'label', 'layer', 'legend', 'li', 'limittext', 'link', 'listing',
'map', 'marquee', 'menu', 'meta', 'multicol', 'nobr', 'noembed', 'noframes', 'noscript', 'nosmartquotes', 'object', 'ol', 'optgroup', 'option',
'param', 'plaintext', 'pre', 'rt', 'ruby', 's', 'samp', 'script', 'select', 'server', 'shadow', 'sidebar', 'small', 'spacer', 'span', 'strike',
'strong', 'style', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'tt', 'ul', 'var', 'wbr', 'xml',
'xmp', '!DOCTYPE', '!--',
];
foreach ($html_tags as $tag) {
// A tag is '<tagname ', so we need to add < and a space or '<tagname>'
if (stristr($xss_check, '<' . $tag . ' ') || stristr($xss_check, '<' . $tag . '>')) {
$app = Factory::getApplication();
$app->enqueueMessage(Text::_('COM_TEMPLATES_ERROR_WARNIEXSS'), 'error');
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,153 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Helper;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Installer\Installer;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Object\CMSObject;
use Joomla\Database\ParameterType;
use Joomla\Filesystem\Path;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Templates component helper.
*
* @since 1.6
*/
class TemplatesHelper
{
/**
* Get a list of filter options for the application clients.
*
* @return array An array of HtmlOption elements.
*/
public static function getClientOptions()
{
// Build the filter options.
$options = [];
$options[] = HTMLHelper::_('select.option', '0', Text::_('JSITE'));
$options[] = HTMLHelper::_('select.option', '1', Text::_('JADMINISTRATOR'));
return $options;
}
/**
* Get a list of filter options for the templates with styles.
*
* @param mixed $clientId The CMS client id (0:site | 1:administrator) or '*' for all.
*
* @return array
*/
public static function getTemplateOptions($clientId = '*')
{
// Build the filter options.
$db = Factory::getDbo();
$query = $db->getQuery(true);
$query->select($db->quoteName('element', 'value'))
->select($db->quoteName('name', 'text'))
->select($db->quoteName('extension_id', 'e_id'))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('template'))
->where($db->quoteName('enabled') . ' = 1')
->order($db->quoteName('client_id') . ' ASC')
->order($db->quoteName('name') . ' ASC');
if ($clientId != '*') {
$clientId = (int) $clientId;
$query->where($db->quoteName('client_id') . ' = :clientid')
->bind(':clientid', $clientId, ParameterType::INTEGER);
}
$db->setQuery($query);
$options = $db->loadObjectList();
return $options;
}
/**
* @param string $templateBaseDir
* @param string $templateDir
*
* @return boolean|CMSObject
*/
public static function parseXMLTemplateFile($templateBaseDir, $templateDir)
{
$data = new CMSObject();
// Check of the xml file exists
$filePath = Path::clean($templateBaseDir . '/templates/' . $templateDir . '/templateDetails.xml');
if (is_file($filePath)) {
$xml = Installer::parseXMLInstallFile($filePath);
if ($xml['type'] != 'template') {
return false;
}
foreach ($xml as $key => $value) {
$data->set($key, $value);
}
}
return $data;
}
/**
* @param integer $clientId
* @param string $templateDir
*
* @return boolean|array
*
* @since 3.0
*/
public static function getPositions($clientId, $templateDir)
{
$positions = [];
$templateBaseDir = $clientId ? JPATH_ADMINISTRATOR : JPATH_SITE;
$filePath = Path::clean($templateBaseDir . '/templates/' . $templateDir . '/templateDetails.xml');
if (is_file($filePath)) {
// Read the file to see if it's a valid component XML file
$xml = simplexml_load_file($filePath);
if (!$xml) {
return false;
}
// Check for a valid XML root tag.
// Extensions use 'extension' as the root tag. Languages use 'metafile' instead
if ($xml->getName() != 'extension' && $xml->getName() != 'metafile') {
unset($xml);
return false;
}
$positions = (array) $xml->positions;
if (isset($positions['position'])) {
$positions = (array) $positions['position'];
} else {
$positions = [];
}
}
return $positions;
}
}

View File

@ -0,0 +1,794 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Model;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\AdminModel;
use Joomla\CMS\Object\CMSObject;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Table\Table;
use Joomla\Database\ParameterType;
use Joomla\Filesystem\Path;
use Joomla\Registry\Registry;
use Joomla\String\StringHelper;
use Joomla\Utilities\ArrayHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template style model.
*
* @since 1.6
*/
class StyleModel extends AdminModel
{
/**
* The help screen key for the module.
*
* @var string
* @since 1.6
*/
protected $helpKey = 'Templates:_Edit_Style';
/**
* The help screen base URL for the module.
*
* @var string
* @since 1.6
*/
protected $helpURL;
/**
* Item cache.
*
* @var array
* @since 1.6
*/
private $_cache = [];
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 3.2
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
$config = array_merge(
[
'event_before_delete' => 'onExtensionBeforeDelete',
'event_after_delete' => 'onExtensionAfterDelete',
'event_before_save' => 'onExtensionBeforeSave',
'event_after_save' => 'onExtensionAfterSave',
'events_map' => ['delete' => 'extension', 'save' => 'extension'],
],
$config
);
parent::__construct($config, $factory);
}
/**
* Method to auto-populate the model state.
*
* @note Calling getState in this method will result in recursion.
*
* @return void
*
* @since 1.6
*/
protected function populateState()
{
$app = Factory::getApplication();
// Load the User state.
$pk = $app->getInput()->getInt('id');
$this->setState('style.id', $pk);
// Load the parameters.
$params = ComponentHelper::getParams('com_templates');
$this->setState('params', $params);
}
/**
* Method to delete rows.
*
* @param array &$pks An array of item ids.
*
* @return boolean Returns true on success, false on failure.
*
* @since 1.6
* @throws \Exception
*/
public function delete(&$pks)
{
$pks = (array) $pks;
$user = $this->getCurrentUser();
$table = $this->getTable();
$context = $this->option . '.' . $this->name;
PluginHelper::importPlugin($this->events_map['delete']);
// Iterate the items to delete each one.
foreach ($pks as $pk) {
if ($table->load($pk)) {
// Access checks.
if (!$user->authorise('core.delete', 'com_templates')) {
throw new \Exception(Text::_('JERROR_CORE_DELETE_NOT_PERMITTED'));
}
// You should not delete a default style
if ($table->home != '0') {
Factory::getApplication()->enqueueMessage(Text::_('COM_TEMPLATES_STYLE_CANNOT_DELETE_DEFAULT_STYLE'), 'error');
return false;
}
// Trigger the before delete event.
$result = Factory::getApplication()->triggerEvent($this->event_before_delete, [$context, $table]);
if (\in_array(false, $result, true) || !$table->delete($pk)) {
$this->setError($table->getError());
return false;
}
// Trigger the after delete event.
Factory::getApplication()->triggerEvent($this->event_after_delete, [$context, $table]);
} else {
$this->setError($table->getError());
return false;
}
}
// Clean cache
$this->cleanCache();
return true;
}
/**
* Method to duplicate styles.
*
* @param array &$pks An array of primary key IDs.
*
* @return boolean True if successful.
*
* @throws \Exception
*/
public function duplicate(&$pks)
{
$user = $this->getCurrentUser();
// Access checks.
if (!$user->authorise('core.create', 'com_templates')) {
throw new \Exception(Text::_('JERROR_CORE_CREATE_NOT_PERMITTED'));
}
$context = $this->option . '.' . $this->name;
// Include the plugins for the save events.
PluginHelper::importPlugin($this->events_map['save']);
$table = $this->getTable();
foreach ($pks as $pk) {
if ($table->load($pk, true)) {
// Reset the id to create a new record.
$table->id = 0;
// Reset the home (don't want dupes of that field).
$table->home = 0;
// Alter the title.
$m = null;
$table->title = $this->generateNewTitle(null, null, $table->title);
if (!$table->check()) {
throw new \Exception($table->getError());
}
// Trigger the before save event.
$result = Factory::getApplication()->triggerEvent($this->event_before_save, [$context, &$table, true]);
if (\in_array(false, $result, true) || !$table->store()) {
throw new \Exception($table->getError());
}
// Trigger the after save event.
Factory::getApplication()->triggerEvent($this->event_after_save, [$context, &$table, true]);
} else {
throw new \Exception($table->getError());
}
}
// Clean cache
$this->cleanCache();
return true;
}
/**
* Method to change the title.
*
* @param integer $categoryId The id of the category.
* @param string $alias The alias.
* @param string $title The title.
*
* @return string New title.
*
* @since 1.7.1
*/
protected function generateNewTitle($categoryId, $alias, $title)
{
// Alter the title
$table = $this->getTable();
while ($table->load(['title' => $title])) {
$title = StringHelper::increment($title);
}
return $title;
}
/**
* Method to get the record form.
*
* @param array $data An optional array of data for the form to interrogate.
* @param boolean $loadData True if the form is to load its own data (default case), false if not.
*
* @return Form A Form object on success, false on failure
*
* @since 1.6
*/
public function getForm($data = [], $loadData = true)
{
// The folder and element vars are passed when saving the form.
if (empty($data)) {
$item = $this->getItem();
$clientId = $item->client_id;
$template = $item->template;
} else {
$clientId = ArrayHelper::getValue($data, 'client_id');
$template = ArrayHelper::getValue($data, 'template');
}
// Add the default fields directory
$baseFolder = $clientId ? JPATH_ADMINISTRATOR : JPATH_SITE;
Form::addFieldPath($baseFolder . '/templates/' . $template . '/field');
// These variables are used to add data from the plugin XML files.
$this->setState('item.client_id', $clientId);
$this->setState('item.template', $template);
// Get the form.
$form = $this->loadForm('com_templates.style', 'style', ['control' => 'jform', 'load_data' => $loadData]);
if (empty($form)) {
return false;
}
// Modify the form based on access controls.
if (!$this->canEditState((object) $data)) {
// Disable fields for display.
$form->setFieldAttribute('home', 'disabled', 'true');
// Disable fields while saving.
// The controller has already verified this is a record you can edit.
$form->setFieldAttribute('home', 'filter', 'unset');
}
return $form;
}
/**
* Method to get the data that should be injected in the form.
*
* @return mixed The data for the form.
*
* @since 1.6
*/
protected function loadFormData()
{
// Check the session for previously entered form data.
$data = Factory::getApplication()->getUserState('com_templates.edit.style.data', []);
if (empty($data)) {
$data = $this->getItem();
}
$this->preprocessData('com_templates.style', $data);
return $data;
}
/**
* Method to get a single record.
*
* @param integer $pk The id of the primary key.
*
* @return mixed Object on success, false on failure.
*/
public function getItem($pk = null)
{
$pk = (!empty($pk)) ? $pk : (int) $this->getState('style.id');
if (!isset($this->_cache[$pk])) {
// Get a row instance.
$table = $this->getTable();
// Attempt to load the row.
$return = $table->load($pk);
// Check for a table object error.
if ($return === false && $table->getError()) {
$this->setError($table->getError());
return false;
}
// Convert to the \Joomla\CMS\Object\CMSObject before adding other data.
$properties = $table->getProperties(1);
$this->_cache[$pk] = ArrayHelper::toObject($properties, CMSObject::class);
// Convert the params field to an array.
$registry = new Registry($table->params);
$this->_cache[$pk]->params = $registry->toArray();
// Get the template XML.
$client = ApplicationHelper::getClientInfo($table->client_id);
$path = Path::clean($client->path . '/templates/' . $table->template . '/templateDetails.xml');
if (file_exists($path)) {
$this->_cache[$pk]->xml = simplexml_load_file($path);
} else {
$this->_cache[$pk]->xml = null;
}
}
return $this->_cache[$pk];
}
/**
* Method to allow derived classes to preprocess the form.
*
* @param Form $form A Form object.
* @param mixed $data The data expected for the form.
* @param string $group The name of the plugin group to import (defaults to "content").
*
* @return void
*
* @since 1.6
* @throws \Exception if there is an error in the form event.
*/
protected function preprocessForm(Form $form, $data, $group = 'content')
{
$clientId = $this->getState('item.client_id');
$template = $this->getState('item.template');
$lang = Factory::getLanguage();
$client = ApplicationHelper::getClientInfo($clientId);
if (!$form->loadFile('style_' . $client->name, true)) {
throw new \Exception(Text::_('JERROR_LOADFILE_FAILED'));
}
$formFile = Path::clean($client->path . '/templates/' . $template . '/templateDetails.xml');
// Load the core and/or local language file(s).
// Default to using parent template language constants
$lang->load('tpl_' . $data->parent, $client->path)
|| $lang->load('tpl_' . $data->parent, $client->path . '/templates/' . $data->parent);
// Apply any, optional, overrides for child template language constants
$lang->load('tpl_' . $template, $client->path)
|| $lang->load('tpl_' . $template, $client->path . '/templates/' . $template);
if (file_exists($formFile)) {
// Get the template form.
if (!$form->loadFile($formFile, false, '//config')) {
throw new \Exception(Text::_('JERROR_LOADFILE_FAILED'));
}
}
// Disable home field if it is default style
if (
(\is_array($data) && \array_key_exists('home', $data) && $data['home'] == '1')
|| (\is_object($data) && isset($data->home) && $data->home == '1')
) {
$form->setFieldAttribute('home', 'readonly', 'true');
}
if ($client->name === 'site' && !Multilanguage::isEnabled()) {
$form->setFieldAttribute('home', 'type', 'radio');
$form->setFieldAttribute('home', 'layout', 'joomla.form.field.radio.switcher');
}
// Attempt to load the xml file.
if (!$xml = simplexml_load_file($formFile)) {
throw new \Exception(Text::_('JERROR_LOADFILE_FAILED'));
}
// Get the help data from the XML file if present.
$help = $xml->xpath('/extension/help');
if (!empty($help)) {
$helpKey = trim((string) $help[0]['key']);
$helpURL = trim((string) $help[0]['url']);
$this->helpKey = $helpKey ?: $this->helpKey;
$this->helpURL = $helpURL ?: $this->helpURL;
}
// Trigger the default form events.
parent::preprocessForm($form, $data, $group);
}
/**
* Method to save the form data.
*
* @param array $data The form data.
*
* @return boolean True on success.
*/
public function save($data)
{
// Detect disabled extension
$extension = Table::getInstance('Extension', 'Joomla\\CMS\\Table\\');
if ($extension->load(['enabled' => 0, 'type' => 'template', 'element' => $data['template'], 'client_id' => $data['client_id']])) {
$this->setError(Text::_('COM_TEMPLATES_ERROR_SAVE_DISABLED_TEMPLATE'));
return false;
}
$app = Factory::getApplication();
$table = $this->getTable();
$pk = (!empty($data['id'])) ? $data['id'] : (int) $this->getState('style.id');
$isNew = true;
// Include the extension plugins for the save events.
PluginHelper::importPlugin($this->events_map['save']);
// Load the row if saving an existing record.
if ($pk > 0) {
$table->load($pk);
$isNew = false;
}
if ($app->getInput()->get('task') == 'save2copy') {
$data['title'] = $this->generateNewTitle(null, null, $data['title']);
$data['home'] = 0;
$data['assigned'] = '';
}
// Bind the data.
if (!$table->bind($data)) {
$this->setError($table->getError());
return false;
}
// Prepare the row for saving
$this->prepareTable($table);
// Check the data.
if (!$table->check()) {
$this->setError($table->getError());
return false;
}
// Trigger the before save event.
$result = Factory::getApplication()->triggerEvent($this->event_before_save, ['com_templates.style', &$table, $isNew]);
// Store the data.
if (\in_array(false, $result, true) || !$table->store()) {
$this->setError($table->getError());
return false;
}
$user = $this->getCurrentUser();
if ($user->authorise('core.edit', 'com_menus') && $table->client_id == 0) {
$n = 0;
$db = $this->getDatabase();
$user = $this->getCurrentUser();
$tableId = (int) $table->id;
$userId = (int) $user->id;
if (!empty($data['assigned']) && \is_array($data['assigned'])) {
$data['assigned'] = ArrayHelper::toInteger($data['assigned']);
// Update the mapping for menu items that this style IS assigned to.
$query = $db->getQuery(true)
->update($db->quoteName('#__menu'))
->set($db->quoteName('template_style_id') . ' = :newtsid')
->whereIn($db->quoteName('id'), $data['assigned'])
->where($db->quoteName('template_style_id') . ' != :tsid')
->where('(' . $db->quoteName('checked_out') . ' IS NULL OR ' . $db->quoteName('checked_out') . ' = :userid)')
->bind(':userid', $userId, ParameterType::INTEGER)
->bind(':newtsid', $tableId, ParameterType::INTEGER)
->bind(':tsid', $tableId, ParameterType::INTEGER);
$db->setQuery($query);
$db->execute();
$n += $db->getAffectedRows();
}
// Remove style mappings for menu items this style is NOT assigned to.
// If unassigned then all existing maps will be removed.
$query = $db->getQuery(true)
->update($db->quoteName('#__menu'))
->set($db->quoteName('template_style_id') . ' = 0');
if (!empty($data['assigned'])) {
$query->whereNotIn($db->quoteName('id'), $data['assigned']);
}
$query->where($db->quoteName('template_style_id') . ' = :templatestyleid')
->where('(' . $db->quoteName('checked_out') . ' IS NULL OR ' . $db->quoteName('checked_out') . ' = :userid)')
->bind(':userid', $userId, ParameterType::INTEGER)
->bind(':templatestyleid', $tableId, ParameterType::INTEGER);
$db->setQuery($query);
$db->execute();
$n += $db->getAffectedRows();
if ($n > 0) {
$app->enqueueMessage(Text::plural('COM_TEMPLATES_MENU_CHANGED', $n));
}
}
// Clean the cache.
$this->cleanCache();
// Trigger the after save event.
Factory::getApplication()->triggerEvent($this->event_after_save, ['com_templates.style', &$table, $isNew]);
$this->setState('style.id', $table->id);
return true;
}
/**
* Method to set a template style as home.
*
* @param integer $id The primary key ID for the style.
*
* @return boolean True if successful.
*
* @throws \Exception
*/
public function setHome($id = 0)
{
$user = $this->getCurrentUser();
$db = $this->getDatabase();
// Access checks.
if (!$user->authorise('core.edit.state', 'com_templates')) {
throw new \Exception(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));
}
$style = $this->getTable();
if (!$style->load((int) $id)) {
throw new \Exception(Text::_('COM_TEMPLATES_ERROR_STYLE_NOT_FOUND'));
}
// Detect disabled extension
$extension = Table::getInstance('Extension', 'Joomla\\CMS\\Table\\');
if ($extension->load(['enabled' => 0, 'type' => 'template', 'element' => $style->template, 'client_id' => $style->client_id])) {
throw new \Exception(Text::_('COM_TEMPLATES_ERROR_SAVE_DISABLED_TEMPLATE'));
}
$clientId = (int) $style->client_id;
$id = (int) $id;
// Reset the home fields for the client_id.
$query = $db->getQuery(true)
->update($db->quoteName('#__template_styles'))
->set($db->quoteName('home') . ' = ' . $db->quote('0'))
->where($db->quoteName('client_id') . ' = :clientid')
->where($db->quoteName('home') . ' = ' . $db->quote('1'))
->bind(':clientid', $clientId, ParameterType::INTEGER);
$db->setQuery($query);
$db->execute();
// Set the new home style.
$query = $db->getQuery(true)
->update($db->quoteName('#__template_styles'))
->set($db->quoteName('home') . ' = ' . $db->quote('1'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$db->setQuery($query);
$db->execute();
// Clean the cache.
$this->cleanCache();
return true;
}
/**
* Method to unset a template style as default for a language.
*
* @param integer $id The primary key ID for the style.
*
* @return boolean True if successful.
*
* @throws \Exception
*/
public function unsetHome($id = 0)
{
$user = $this->getCurrentUser();
$db = $this->getDatabase();
// Access checks.
if (!$user->authorise('core.edit.state', 'com_templates')) {
throw new \Exception(Text::_('JLIB_APPLICATION_ERROR_EDITSTATE_NOT_PERMITTED'));
}
$id = (int) $id;
// Lookup the client_id.
$query = $db->getQuery(true)
->select($db->quoteName(['client_id', 'home']))
->from($db->quoteName('#__template_styles'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$db->setQuery($query);
$style = $db->loadObject();
if (!is_numeric($style->client_id)) {
throw new \Exception(Text::_('COM_TEMPLATES_ERROR_STYLE_NOT_FOUND'));
}
if ($style->home == '1') {
throw new \Exception(Text::_('COM_TEMPLATES_ERROR_CANNOT_UNSET_DEFAULT_STYLE'));
}
// Set the new home style.
$query = $db->getQuery(true)
->update($db->quoteName('#__template_styles'))
->set($db->quoteName('home') . ' = ' . $db->quote('0'))
->where($db->quoteName('id') . ' = :id')
->bind(':id', $id, ParameterType::INTEGER);
$db->setQuery($query);
$db->execute();
// Clean the cache.
$this->cleanCache();
return true;
}
/**
* Get the necessary data to load an item help screen.
*
* @return object An object with key, url, and local properties for loading the item help screen.
*
* @since 1.6
*/
public function getHelp()
{
return (object) ['key' => $this->helpKey, 'url' => $this->helpURL];
}
/**
* Returns the back end template for the given style.
*
* @param int $styleId The style id
*
* @return \stdClass
*
* @since 4.2.0
*/
public function getAdminTemplate(int $styleId): \stdClass
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['s.template', 's.params', 's.inheritable', 's.parent']))
->from($db->quoteName('#__template_styles', 's'))
->join(
'LEFT',
$db->quoteName('#__extensions', 'e'),
$db->quoteName('e.type') . ' = ' . $db->quote('template')
. ' AND ' . $db->quoteName('e.element') . ' = ' . $db->quoteName('s.template')
. ' AND ' . $db->quoteName('e.client_id') . ' = ' . $db->quoteName('s.client_id')
)
->where(
[
$db->quoteName('s.client_id') . ' = 1',
$db->quoteName('s.home') . ' = ' . $db->quote('1'),
]
);
if ($styleId) {
$query->extendWhere(
'OR',
[
$db->quoteName('s.client_id') . ' = 1',
$db->quoteName('s.id') . ' = :style',
$db->quoteName('e.enabled') . ' = 1',
]
)
->bind(':style', $styleId, ParameterType::INTEGER);
}
$query->order($db->quoteName('s.home'));
$db->setQuery($query);
return $db->loadObject();
}
/**
* Returns the front end templates.
*
* @return array
*
* @since 4.2.0
*/
public function getSiteTemplates(): array
{
$db = $this->getDatabase();
$query = $db->getQuery(true)
->select($db->quoteName(['id', 'home', 'template', 's.params', 'inheritable', 'parent']))
->from($db->quoteName('#__template_styles', 's'))
->where(
[
$db->quoteName('s.client_id') . ' = 0',
$db->quoteName('e.enabled') . ' = 1',
]
)
->join(
'LEFT',
$db->quoteName('#__extensions', 'e'),
$db->quoteName('e.element') . ' = ' . $db->quoteName('s.template')
. ' AND ' . $db->quoteName('e.type') . ' = ' . $db->quote('template')
. ' AND ' . $db->quoteName('e.client_id') . ' = ' . $db->quoteName('s.client_id')
);
$db->setQuery($query);
return $db->loadObjectList('id');
}
/**
* Custom clean cache method
*
* @param string $group The cache group
* @param integer $clientId No longer used, will be removed without replacement
* @deprecated 4.3 will be removed in 6.0
*
* @return void
*
* @since 1.6
*/
protected function cleanCache($group = null, $clientId = 0)
{
parent::cleanCache('com_templates');
parent::cleanCache('_system');
}
}

View File

@ -0,0 +1,258 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Model;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Database\ParameterType;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of template style records.
*
* @since 1.6
*/
class StylesModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 3.2
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'a.id',
'title', 'a.title',
'template', 'a.template',
'home', 'a.home',
'menuitem',
];
}
parent::__construct($config, $factory);
}
/**
* 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 1.6
*/
protected function populateState($ordering = 'a.template', $direction = 'asc')
{
$app = Factory::getApplication();
if (!$app->isClient('api')) {
// Load the filter state.
$this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string'));
$this->setState('filter.template', $this->getUserStateFromRequest($this->context . '.filter.template', 'filter_template', '', 'string'));
$this->setState('filter.menuitem', $this->getUserStateFromRequest($this->context . '.filter.menuitem', 'filter_menuitem', '', 'cmd'));
// Special case for the client id.
$clientId = (int) $this->getUserStateFromRequest($this->context . '.client_id', 'client_id', 0, 'int');
$clientId = !\in_array($clientId, [0, 1]) ? 0 : $clientId;
$this->setState('client_id', $clientId);
}
// Load the parameters.
$params = ComponentHelper::getParams('com_templates');
$this->setState('params', $params);
// 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.
*/
protected function getStoreId($id = '')
{
// Compile the store id.
$id .= ':' . $this->getState('client_id');
$id .= ':' . $this->getState('filter.search');
$id .= ':' . $this->getState('filter.template');
$id .= ':' . $this->getState('filter.menuitem');
return parent::getStoreId($id);
}
/**
* Build an SQL query to load the list data.
*
* @return \Joomla\Database\DatabaseQuery
*/
protected function getListQuery()
{
$clientId = (int) $this->getState('client_id');
// 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',
[
$db->quoteName('a.id'),
$db->quoteName('a.template'),
$db->quoteName('a.title'),
$db->quoteName('a.home'),
$db->quoteName('a.client_id'),
$db->quoteName('l.title', 'language_title'),
$db->quoteName('l.image'),
$db->quoteName('l.sef', 'language_sef'),
]
)
)
->select(
[
'COUNT(' . $db->quoteName('m.template_style_id') . ') AS assigned',
$db->quoteName('extension_id', 'e_id'),
]
)
->from($db->quoteName('#__template_styles', 'a'))
->where($db->quoteName('a.client_id') . ' = :clientid')
->bind(':clientid', $clientId, ParameterType::INTEGER);
// Join on menus.
$query->join('LEFT', $db->quoteName('#__menu', 'm'), $db->quoteName('m.template_style_id') . ' = ' . $db->quoteName('a.id'))
->group(
[
$db->quoteName('a.id'),
$db->quoteName('a.template'),
$db->quoteName('a.title'),
$db->quoteName('a.home'),
$db->quoteName('a.client_id'),
$db->quoteName('l.title'),
$db->quoteName('l.image'),
$db->quoteName('l.sef'),
$db->quoteName('e.extension_id'),
]
);
// Join over the language.
$query->join('LEFT', $db->quoteName('#__languages', 'l'), $db->quoteName('l.lang_code') . ' = ' . $db->quoteName('a.home'));
// Filter by extension enabled.
$query->join(
'LEFT',
$db->quoteName('#__extensions', 'e'),
$db->quoteName('e.element') . ' = ' . $db->quoteName('a.template')
. ' AND ' . $db->quoteName('e.client_id') . ' = ' . $db->quoteName('a.client_id')
)
->where(
[
$db->quoteName('e.enabled') . ' = 1',
$db->quoteName('e.type') . ' = ' . $db->quote('template'),
]
);
// Filter by template.
if ($template = $this->getState('filter.template')) {
$query->where($db->quoteName('a.template') . ' = :template')
->bind(':template', $template);
}
// Filter by menuitem.
$menuItemId = $this->getState('filter.menuitem');
if ($clientId === 0 && is_numeric($menuItemId)) {
// If user selected the templates styles that are not assigned to any page.
if ((int) $menuItemId === -1) {
// Only custom template styles overrides not assigned to any menu item.
$query->where(
[
$db->quoteName('a.home') . ' = ' . $db->quote('0'),
$db->quoteName('m.id') . ' IS NULL',
]
);
} else {
// If user selected the templates styles assigned to particular pages.
// Subquery to get the language of the selected menu item.
$menuItemId = (int) $menuItemId;
$menuItemLanguageSubQuery = $db->getQuery(true);
$menuItemLanguageSubQuery->select($db->quoteName('language'))
->from($db->quoteName('#__menu'))
->where($db->quoteName('id') . ' = :menuitemid');
$query->bind(':menuitemid', $menuItemId, ParameterType::INTEGER);
// Subquery to get the language of the selected menu item.
$templateStylesMenuItemsSubQuery = $db->getQuery(true);
$templateStylesMenuItemsSubQuery->select($db->quoteName('id'))
->from($db->quoteName('#__menu'))
->where($db->quoteName('template_style_id') . ' = ' . $db->quoteName('a.id'));
// Main query where clause.
$query->where('(' .
// Default template style (fallback template style to all menu items).
$db->quoteName('a.home') . ' = ' . $db->quote('1') . ' OR ' .
// Default template style for specific language (fallback template style to the selected menu item language).
$db->quoteName('a.home') . ' IN (' . $menuItemLanguageSubQuery . ') OR ' .
// Custom template styles override (only if assigned to the selected menu item).
'(' . $db->quoteName('a.home') . ' = ' . $db->quote('0') . ' AND ' . $menuItemId . ' IN (' . $templateStylesMenuItemsSubQuery . '))' .
')');
}
}
// Filter by search in title.
if ($search = $this->getState('filter.search')) {
if (stripos($search, 'id:') === 0) {
$ids = (int) substr($search, 3);
$query->where($db->quoteName('a.id') . ' = :id');
$query->bind(':id', $ids, ParameterType::INTEGER);
} else {
$search = '%' . StringHelper::strtolower($search) . '%';
$query->extendWhere(
'AND',
[
'LOWER(' . $db->quoteName('a.template') . ') LIKE :templatesearch',
'LOWER(' . $db->quoteName('a.title') . ') LIKE :title',
],
'OR'
)
->bind(':templatesearch', $search)
->bind(':title', $search);
}
}
// Add the list ordering clause.
$query->order($db->escape($this->getState('list.ordering', 'a.template')) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));
return $query;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,223 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Model;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\MVC\Factory\MVCFactoryInterface;
use Joomla\CMS\MVC\Model\ListModel;
use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper;
use Joomla\Database\ParameterType;
use Joomla\String\StringHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Methods supporting a list of template extension records.
*
* @since 1.6
*/
class TemplatesModel extends ListModel
{
/**
* Constructor.
*
* @param array $config An optional associative array of configuration settings.
* @param MVCFactoryInterface $factory The factory.
*
* @see \Joomla\CMS\MVC\Model\BaseDatabaseModel
* @since 3.2
*/
public function __construct($config = [], MVCFactoryInterface $factory = null)
{
if (empty($config['filter_fields'])) {
$config['filter_fields'] = [
'id', 'a.id',
'name', 'a.name',
'folder', 'a.folder',
'element', 'a.element',
'checked_out', 'a.checked_out',
'checked_out_time', 'a.checked_out_time',
'state', 'a.state',
'enabled', 'a.enabled',
'ordering', 'a.ordering',
];
}
parent::__construct($config, $factory);
}
/**
* Override parent getItems to add extra XML metadata.
*
* @return array
*
* @since 1.6
*/
public function getItems()
{
$items = parent::getItems();
foreach ($items as &$item) {
$client = ApplicationHelper::getClientInfo($item->client_id);
$item->xmldata = TemplatesHelper::parseXMLTemplateFile($client->path, $item->element);
$num = $this->updated($item->extension_id);
if ($num) {
$item->updated = $num;
}
}
return $items;
}
/**
* Check if template extension have any updated override.
*
* @param integer $exid Extension id of template.
*
* @return boolean False if records not found/else integer.
*
* @since 4.0.0
*/
public function updated($exid)
{
$db = $this->getDatabase();
// Select the required fields from the table
$query = $db->getQuery(true)
->select($db->quoteName('template'))
->from($db->quoteName('#__template_overrides'))
->where($db->quoteName('extension_id') . ' = :extensionid')
->where($db->quoteName('state') . ' = 0')
->bind(':extensionid', $exid, ParameterType::INTEGER);
// Reset the query.
$db->setQuery($query);
// Load the results as a list of stdClass objects.
$num = \count($db->loadObjectList());
if ($num > 0) {
return $num;
}
return false;
}
/**
* Build an SQL query to load the list data.
*
* @return \Joomla\Database\DatabaseQuery
*
* @since 1.6
*/
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.extension_id, a.name, a.element, a.client_id'
)
);
$clientId = (int) $this->getState('client_id');
$query->from($db->quoteName('#__extensions', 'a'))
->where($db->quoteName('a.client_id') . ' = :clientid')
->where($db->quoteName('a.enabled') . ' = 1')
->where($db->quoteName('a.type') . ' = ' . $db->quote('template'))
->bind(':clientid', $clientId, ParameterType::INTEGER);
// Filter by search in title.
if ($search = $this->getState('filter.search')) {
if (stripos($search, 'id:') === 0) {
$ids = (int) substr($search, 3);
$query->where($db->quoteName('a.id') . ' = :id');
$query->bind(':id', $ids, ParameterType::INTEGER);
} else {
$search = '%' . StringHelper::strtolower($search) . '%';
$query->extendWhere(
'AND',
[
'LOWER(' . $db->quoteName('a.element') . ') LIKE :element',
'LOWER(' . $db->quoteName('a.name') . ') LIKE :name',
],
'OR'
)
->bind(':element', $search)
->bind(':name', $search);
}
}
// Add the list ordering clause.
$query->order($db->escape($this->getState('list.ordering', 'a.element')) . ' ' . $db->escape($this->getState('list.direction', 'ASC')));
return $query;
}
/**
* 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 1.6
*/
protected function getStoreId($id = '')
{
// Compile the store id.
$id .= ':' . $this->getState('client_id');
$id .= ':' . $this->getState('filter.search');
return parent::getStoreId($id);
}
/**
* 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 1.6
*/
protected function populateState($ordering = 'a.element', $direction = 'asc')
{
// Load the filter state.
$this->setState('filter.search', $this->getUserStateFromRequest($this->context . '.filter.search', 'filter_search', '', 'string'));
// Special case for the client id.
$clientId = (int) $this->getUserStateFromRequest($this->context . '.client_id', 'client_id', 0, 'int');
$clientId = (!\in_array($clientId, [0, 1])) ? 0 : $clientId;
$this->setState('client_id', $clientId);
// Load the parameters.
$params = ComponentHelper::getParams('com_templates');
$this->setState('params', $params);
// List state information.
parent::populateState($ordering, $direction);
}
}

View File

@ -0,0 +1,165 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Service\HTML;
use Joomla\CMS\Application\ApplicationHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Html helper class.
*
* @since 1.6
*/
class Templates
{
/**
* Display the thumb for the template.
*
* @param string|object $template The name of the template or the template object.
* @deprecated 4.3 will be removed in 6.0
* The argument $template must be an object only
* @param integer $clientId No longer used, will be removed without replacement
* @deprecated 4.3 will be removed in 6.0
*
* @return string The html string
*
* @since 1.6
*/
public function thumb($template, $clientId = 0)
{
if (\is_string($template)) {
return HTMLHelper::_('image', 'template_thumbnail.png', Text::_('COM_TEMPLATES_PREVIEW'), [], true, -1);
}
$client = ApplicationHelper::getClientInfo($template->client_id);
if (!isset($template->xmldata)) {
$template->xmldata = TemplatesHelper::parseXMLTemplateFile($client->id === 0 ? JPATH_ROOT : JPATH_ROOT . '/administrator', $template->name);
}
if ((isset($template->xmldata->inheritable) && (bool) $template->xmldata->inheritable) || isset($template->xmldata->parent)) {
if (isset($template->xmldata->parent) && (string) $template->xmldata->parent !== '' && file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . (string) $template->xmldata->parent . '/images/template_thumbnail.png')) {
if (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_preview.png')) {
$html = HTMLHelper::_('image', 'media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png', Text::_('COM_TEMPLATES_PREVIEW'));
$html = '<button type="button" data-bs-target="#' . $template->element . '-Modal" class="thumbnail" data-bs-toggle="modal" title="' . Text::_('COM_TEMPLATES_CLICK_TO_ENLARGE') . '">' . $html . '</button>';
} elseif ((file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . (string) $template->xmldata->parent . '/images/template_preview.png'))) {
$html = HTMLHelper::_('image', 'media/templates/' . $client->name . '/' . (string) $template->xmldata->parent . '/images/template_thumbnail.png', Text::_('COM_TEMPLATES_PREVIEW'));
$html = '<button type="button" data-bs-target="#' . $template->element . '-Modal" class="thumbnail" data-bs-toggle="modal" title="' . Text::_('COM_TEMPLATES_CLICK_TO_ENLARGE') . '">' . $html . '</button>';
} else {
$html = HTMLHelper::_('image', 'template_thumb.svg', Text::_('COM_TEMPLATES_PREVIEW'), ['style' => 'width:200px; height:120px;']);
}
} elseif (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png')) {
$html = HTMLHelper::_('image', 'media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png', Text::_('COM_TEMPLATES_PREVIEW'));
if (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_preview.png')) {
$html = '<button type="button" data-bs-target="#' . $template->element . '-Modal" class="thumbnail" data-bs-toggle="modal" title="' . Text::_('COM_TEMPLATES_CLICK_TO_ENLARGE') . '">' . $html . '</button>';
}
} else {
$html = HTMLHelper::_('image', 'template_thumb.svg', Text::_('COM_TEMPLATES_PREVIEW'), ['style' => 'width:200px; height:120px;']);
}
} elseif (file_exists($client->path . '/templates/' . $template->element . '/template_thumbnail.png')) {
$html = HTMLHelper::_('image', (($template->client_id == 0) ? Uri::root(true) : Uri::root(true) . '/administrator/') . '/templates/' . $template->element . '/template_thumbnail.png', Text::_('COM_TEMPLATES_PREVIEW'), [], false, -1);
if (file_exists($client->path . '/templates/' . $template->element . '/template_preview.png')) {
$html = '<button type="button" data-bs-target="#' . $template->element . '-Modal" class="thumbnail" data-bs-toggle="modal" title="' . Text::_('COM_TEMPLATES_CLICK_TO_ENLARGE') . '">' . $html . '</button>';
}
} else {
$html = HTMLHelper::_('image', 'template_thumb.svg', Text::_('COM_TEMPLATES_PREVIEW'), ['style' => 'width:200px; height:120px;']);
}
return $html;
}
/**
* Renders the html for the modal linked to thumb.
*
* @param string|object $template The name of the template or the template object.
* @deprecated 4.3 will be removed in 6.0
* The argument $template must be an object only
* @param integer $clientId No longer used, will be removed without replacement
* @deprecated 4.3 will be removed in 6.0
*
* @return string The html string
*
* @since 3.4
*/
public function thumbModal($template, $clientId = 0)
{
if (\is_string($template)) {
return HTMLHelper::_('image', 'template_thumbnail.png', Text::_('COM_TEMPLATES_PREVIEW'), [], true, -1);
}
$html = '';
$thumb = '';
$preview = '';
$client = ApplicationHelper::getClientInfo($template->client_id);
if (!isset($template->xmldata)) {
$template->xmldata = TemplatesHelper::parseXMLTemplateFile($client->id === 0 ? JPATH_ROOT : JPATH_ROOT . '/administrator', $template->name);
}
if ((isset($template->xmldata->inheritable) && (bool) $template->xmldata->inheritable) || isset($template->xmldata->parent)) {
if (isset($template->xmldata->parent) && (string) $template->xmldata->parent !== '') {
if (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png')) {
$thumb = ($template->client_id == 0 ? Uri::root(true) : Uri::root(true) . 'administrator') . 'media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png';
if (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_preview.png')) {
$preview = ($template->client_id == 0 ? Uri::root(true) : Uri::root(true) . '/administrator') . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_preview.png';
}
} else {
$thumb = ($template->client_id == 0 ? Uri::root(true) : Uri::root(true) . 'administrator') . 'media/templates/' . $client->name . '/' . (string) $template->xmldata->parent . '/images/template_thumbnail.png';
if (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . (string) $template->xmldata->parent . '/images/template_preview.png')) {
$preview = ($template->client_id == 0 ? Uri::root(true) : Uri::root(true) . '/administrator') . '/media/templates/' . $client->name . '/' . (string) $template->xmldata->parent . '/images/template_preview.png';
}
}
} elseif (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png')) {
$thumb = ($template->client_id == 0 ? Uri::root(true) : Uri::root(true) . '/administrator') . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_thumbnail.png';
if (file_exists(JPATH_ROOT . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_preview.png')) {
$preview = Uri::root(true) . '/media/templates/' . $client->name . '/' . $template->element . '/images/template_preview.png';
}
}
} elseif (file_exists($client->path . '/templates/' . $template->element . '/template_thumbnail.png')) {
$thumb = (($template->client_id == 0) ? Uri::root(true) : Uri::root(true) . 'administrator') . '/templates/' . $template->element . '/template_thumbnail.png';
if (file_exists($client->path . '/templates/' . $template->element . '/template_preview.png')) {
$preview = (($template->client_id == 0) ? Uri::root(true) : Uri::root(true) . '/administrator') . '/templates/' . $template->element . '/template_preview.png';
}
}
if ($thumb !== '' && $preview !== '') {
$footer = '<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">'
. Text::_('JTOOLBAR_CLOSE') . '</button>';
$html .= HTMLHelper::_(
'bootstrap.renderModal',
$template->element . '-Modal',
[
'title' => Text::sprintf('COM_TEMPLATES_SCREENSHOT', ucfirst($template->name)),
'height' => '500px',
'width' => '800px',
'footer' => $footer,
],
'<div><img src="' . $preview . '" class="mw-100" alt="' . $template->name . '"></div>'
);
}
return $html;
}
}

View File

@ -0,0 +1,159 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\Table;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Table\Table;
use Joomla\Database\DatabaseDriver;
use Joomla\Database\ParameterType;
use Joomla\Event\DispatcherInterface;
use Joomla\Registry\Registry;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Template style table class.
*
* @since 1.6
*/
class StyleTable extends Table
{
/**
* Constructor
*
* @param DatabaseDriver $db Database connector object
* @param ?DispatcherInterface $dispatcher Event dispatcher for this table
*
* @since 1.6
*/
public function __construct(DatabaseDriver $db, DispatcherInterface $dispatcher = null)
{
parent::__construct('#__template_styles', 'id', $db, $dispatcher);
}
/**
* Overloaded bind function to pre-process the params.
*
* @param array $array Named array
* @param mixed $ignore An optional array or space separated list of properties to ignore while binding.
*
* @return null|string null if operation was satisfactory, otherwise returns an error
*
* @since 1.6
*/
public function bind($array, $ignore = '')
{
if (isset($array['params']) && \is_array($array['params'])) {
$registry = new Registry($array['params']);
$array['params'] = (string) $registry;
}
// Verify that the default style is not unset
if ($array['home'] == '0' && $this->home == '1') {
$this->setError(Text::_('COM_TEMPLATES_ERROR_CANNOT_UNSET_DEFAULT_STYLE'));
return false;
}
return parent::bind($array, $ignore);
}
/**
* Overloaded check method to ensure data integrity.
*
* @return boolean True on success.
*
* @since 1.6
*/
public function check()
{
try {
parent::check();
} catch (\Exception $e) {
$this->setError($e->getMessage());
return false;
}
if (empty($this->title)) {
$this->setError(Text::_('COM_TEMPLATES_ERROR_STYLE_REQUIRES_TITLE'));
return false;
}
return true;
}
/**
* Overloaded store method to ensure unicity of default style.
*
* @param boolean $updateNulls True to update fields even if they are null.
*
* @return boolean True on success.
*
* @since 1.6
*/
public function store($updateNulls = false)
{
if ($this->home != '0') {
$clientId = (int) $this->client_id;
$query = $this->_db->getQuery(true)
->update($this->_db->quoteName('#__template_styles'))
->set($this->_db->quoteName('home') . ' = ' . $this->_db->quote('0'))
->where($this->_db->quoteName('client_id') . ' = :clientid')
->where($this->_db->quoteName('home') . ' = :home')
->bind(':clientid', $clientId, ParameterType::INTEGER)
->bind(':home', $this->home);
$this->_db->setQuery($query);
$this->_db->execute();
}
return parent::store($updateNulls);
}
/**
* Overloaded store method to unsure existence of a default style for a template.
*
* @param mixed $pk An optional primary key value to delete. If not set the instance property value is used.
*
* @return boolean True on success.
*
* @since 1.6
*/
public function delete($pk = null)
{
$k = $this->_tbl_key;
$pk = \is_null($pk) ? $this->$k : $pk;
if (!\is_null($pk)) {
$clientId = (int) $this->client_id;
$query = $this->_db->getQuery(true)
->select($this->_db->quoteName('id'))
->from($this->_db->quoteName('#__template_styles'))
->where($this->_db->quoteName('client_id') . ' = :clientid')
->where($this->_db->quoteName('template') . ' = :template')
->bind(':template', $this->template)
->bind(':clientid', $clientId, ParameterType::INTEGER);
$this->_db->setQuery($query);
$results = $this->_db->loadColumn();
if (\count($results) == 1 && $results[0] == $pk) {
$this->setError(Text::_('COM_TEMPLATES_ERROR_CANNOT_DELETE_LAST_STYLE'));
return false;
}
}
return parent::delete($pk);
}
}

View File

@ -0,0 +1,153 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\View\Style;
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\Object\CMSObject;
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 a template style.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* The CMSObject (on success, false on failure)
*
* @var CMSObject
*/
protected $item;
/**
* The form object
*
* @var \Joomla\CMS\Form\Form
*/
protected $form;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* The actions the user is authorised to perform
*
* @var \Joomla\Registry\Registry
*
* @since 4.0.0
*/
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
*
* @since 1.6
*/
public function display($tpl = null)
{
$this->item = $this->get('Item');
$this->state = $this->get('State');
$this->form = $this->get('Form');
$this->canDo = ContentHelper::getActions('com_templates');
// Check for errors.
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
*
* @since 1.6
*/
protected function addToolbar()
{
Factory::getApplication()->getInput()->set('hidemainmenu', true);
$isNew = ($this->item->id == 0);
$canDo = $this->canDo;
$toolbar = Toolbar::getInstance();
ToolbarHelper::title(
$isNew ? Text::_('COM_TEMPLATES_MANAGER_ADD_STYLE')
: Text::_('COM_TEMPLATES_MANAGER_EDIT_STYLE'),
'paint-brush thememanager'
);
// If not checked out, can save the item.
if ($canDo->get('core.edit')) {
$toolbar->apply('style.apply');
}
$saveGroup = $toolbar->dropdownButton('save-group');
$saveGroup->configure(
function (Toolbar $childBar) use ($canDo, $isNew) {
// If not checked out, can save the item.
if ($canDo->get('core.edit')) {
$childBar->save('style.save');
}
// If an existing item, can save to a copy.
if (!$isNew && $canDo->get('core.create')) {
$childBar->save2copy('style.save2copy');
}
}
);
if (empty($this->item->id)) {
$toolbar->cancel('style.cancel', 'JTOOLBAR_CANCEL');
} else {
$toolbar->cancel('style.cancel');
}
$toolbar->divider();
// Get the help information for the template item.
$lang = $this->getLanguage();
$help = $this->get('Help');
if ($lang->hasKey($help->url)) {
$debug = $lang->setDebug(false);
$url = Text::_($help->url);
$lang->setDebug($debug);
} else {
$url = null;
}
$toolbar->help($help->key, false, $url);
}
}

View File

@ -0,0 +1,77 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\View\Style;
use Joomla\CMS\Factory;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Object\CMSObject;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View to edit a template style.
*
* @since 1.6
*/
class JsonView extends BaseHtmlView
{
/**
* The CMSObject (on success, false on failure)
*
* @var CMSObject
*/
protected $item;
/**
* The form object
*
* @var \Joomla\CMS\Form\Form
*/
protected $form;
/**
* The model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @since 1.6
*/
public function display($tpl = null)
{
try {
$this->item = $this->get('Item');
} catch (\Exception $e) {
$app = Factory::getApplication();
$app->enqueueMessage($e->getMessage(), 'error');
return false;
}
$paramsList = $this->item->getProperties();
unset($paramsList['xml']);
$paramsList = json_encode($paramsList);
return $paramsList;
}
}

View File

@ -0,0 +1,162 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\View\Styles;
use Joomla\CMS\Component\ComponentHelper;
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 class for a list of template styles.
*
* @since 1.6
*/
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
*
* @since 4.0.0
*/
public $filterForm;
/**
* The active search filters
*
* @var array
* @since 4.0.0
*/
public $activeFilters;
/**
* Is the parameter enabled to show template positions in the frontend?
*
* @var boolean
* @since 4.0.0
*/
public $preview;
/**
* 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
*/
public function display($tpl = null)
{
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->total = $this->get('Total');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
$this->preview = ComponentHelper::getParams('com_templates')->get('template_positions_display');
// Remove the menu item filter for administrator styles.
if ((int) $this->state->get('client_id') !== 0) {
unset($this->activeFilters['menuitem']);
$this->filterForm->removeField('menuitem', 'filter');
}
// Check for errors.
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
*
* @since 1.6
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_templates');
$clientId = (int) $this->get('State')->get('client_id');
$toolbar = Toolbar::getInstance();
// Add a shortcut to the templates list view.
$toolbar->linkButton('templates', 'COM_TEMPLATES_MANAGER_TEMPLATES')
->url('index.php?option=com_templates&view=templates&client_id=' . $clientId)
->icon('icon-code thememanager');
// Set the title.
if ($clientId === 1) {
ToolbarHelper::title(Text::_('COM_TEMPLATES_MANAGER_STYLES_ADMIN'), 'paint-brush thememanager');
} else {
ToolbarHelper::title(Text::_('COM_TEMPLATES_MANAGER_STYLES_SITE'), 'paint-brush thememanager');
}
if ($canDo->get('core.edit.state')) {
$toolbar->makeDefault('styles.setDefault', 'COM_TEMPLATES_TOOLBAR_SET_HOME');
$toolbar->divider();
}
if ($canDo->get('core.create')) {
$toolbar->standardButton('duplicate', 'JTOOLBAR_DUPLICATE', 'styles.duplicate')
->listCheck(true)
->icon('icon-copy');
$toolbar->divider();
}
if ($canDo->get('core.delete')) {
$toolbar->delete('styles.delete')
->message('JGLOBAL_CONFIRM_DELETE')
->listCheck(true);
$toolbar->divider();
}
if ($canDo->get('core.admin') || $canDo->get('core.options')) {
$toolbar->preferences('com_templates');
$toolbar->divider();
}
$toolbar->help('Templates:_Styles');
}
}

View File

@ -0,0 +1,413 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\View\Template;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Language\Text;
use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Toolbar\Button\DropdownButton;
use Joomla\CMS\Toolbar\Toolbar;
use Joomla\CMS\Toolbar\ToolbarHelper;
use Joomla\CMS\Uri\Uri;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* View to edit a template.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* The Model state
*
* @var \Joomla\Registry\Registry
*/
protected $state;
/**
* The template details
*
* @var \stdClass|false
*/
protected $template;
/**
* For loading the source form
*
* @var Form
*/
protected $form;
/**
* For loading source file contents
*
* @var array
*/
protected $source;
/**
* Extension id
*
* @var integer
*/
protected $id;
/**
* Encrypted file path
*
* @var string
*/
protected $file;
/**
* List of available overrides
*
* @var array
*/
protected $overridesList;
/**
* Name of the present file
*
* @var string
*/
protected $fileName;
/**
* Type of the file - image, source, font
*
* @var string
*/
protected $type;
/**
* For loading image information
*
* @var array
*/
protected $image;
/**
* Template id for showing preview button
*
* @var \stdClass
*/
protected $preview;
/**
* For loading font information
*
* @var array
*/
protected $font;
/**
* A nested array containing list of files and folders
*
* @var array
*/
protected $files;
/**
* An array containing a list of compressed files
*
* @var array
*/
protected $archive;
/**
* The state of installer override plugin.
*
* @var array
*
* @since 4.0.0
*/
protected $pluginState;
/**
* A nested array containing list of files and folders in the media folder
*
* @var array
*
* @since 4.1.0
*/
protected $mediaFiles;
/**
* 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|boolean
*/
public function display($tpl = null)
{
$app = Factory::getApplication();
$this->file = $app->getInput()->get('file', '');
$this->fileName = InputFilter::getInstance()->clean(base64_decode($this->file), 'string');
$explodeArray = explode('.', $this->fileName);
$ext = end($explodeArray);
$this->files = $this->get('Files');
$this->mediaFiles = $this->get('MediaFiles');
$this->state = $this->get('State');
$this->template = $this->get('Template');
$this->preview = $this->get('Preview');
$this->pluginState = PluginHelper::isEnabled('installer', 'override');
$this->updatedList = $this->get('UpdatedList');
$this->styles = $this->get('AllTemplateStyles');
$this->stylesHTML = '';
$params = ComponentHelper::getParams('com_templates');
$imageTypes = explode(',', $params->get('image_formats', 'gif,bmp,jpg,jpeg,png,webp'));
$sourceTypes = explode(',', $params->get('source_formats', 'txt,less,ini,xml,js,php,css,scss,sass,json'));
$fontTypes = explode(',', $params->get('font_formats', 'woff,woff2,ttf,otf'));
$archiveTypes = explode(',', $params->get('compressed_formats', 'zip'));
if (\in_array($ext, $sourceTypes)) {
$this->form = $this->get('Form');
$this->form->setFieldAttribute('source', 'syntax', $ext);
$this->source = $this->get('Source');
$this->type = 'file';
} elseif (\in_array($ext, $imageTypes)) {
try {
$this->image = $this->get('Image');
$this->type = 'image';
} catch (\RuntimeException $exception) {
$app->enqueueMessage(Text::_('COM_TEMPLATES_GD_EXTENSION_NOT_AVAILABLE'));
$this->type = 'home';
}
} elseif (\in_array($ext, $fontTypes)) {
$this->font = $this->get('Font');
$this->type = 'font';
} elseif (\in_array($ext, $archiveTypes)) {
$this->archive = $this->get('Archive');
$this->type = 'archive';
} else {
$this->type = 'home';
}
$this->overridesList = $this->get('OverridesList');
$this->id = $this->state->get('extension.id');
// Check for errors.
if (\count($errors = $this->get('Errors'))) {
$app->enqueueMessage(implode("\n", $errors));
return false;
}
$this->addToolbar();
if (!$this->getCurrentUser()->authorise('core.admin')) {
$this->setLayout('readonly');
}
parent::display($tpl);
}
/**
* Add the page title and toolbar.
*
* @since 1.6
*
* @return void
*/
protected function addToolbar()
{
$app = Factory::getApplication();
$user = $this->getCurrentUser();
$toolbar = Toolbar::getInstance('toolbar');
$app->getInput()->set('hidemainmenu', true);
// User is global SuperUser
$isSuperUser = $user->authorise('core.admin');
$explodeArray = explode('.', $this->fileName);
$ext = end($explodeArray);
ToolbarHelper::title(Text::sprintf('COM_TEMPLATES_MANAGER_VIEW_TEMPLATE', ucfirst($this->template->name)), 'icon-code thememanager');
// Only show file edit buttons for global SuperUser
if ($isSuperUser) {
// Add an Apply and save button
if ($this->type === 'file') {
$toolbar->apply('template.apply');
$toolbar->save('template.save');
} elseif ($this->type === 'image') {
// Add a Crop and Resize button
$toolbar->standardButton('crop', 'COM_TEMPLATES_BUTTON_CROP', 'template.cropImage')
->listCheck(false)
->icon('icon-crop');
ToolbarHelper::modal('resizeModal', 'icon-expand', 'COM_TEMPLATES_BUTTON_RESIZE');
} elseif ($this->type === 'archive') {
// Add an extract button
$toolbar->standardButton('extract', 'COM_TEMPLATES_BUTTON_EXTRACT_ARCHIVE', 'template.extractArchive')
->listCheck(false)
->icon('icon-chevron-down');
} elseif ($this->type === 'home') {
// Add a copy/child template button
if (isset($this->template->xmldata->inheritable) && (string) $this->template->xmldata->inheritable === '1') {
ToolbarHelper::modal('childModal', 'icon-copy', 'COM_TEMPLATES_BUTTON_TEMPLATE_CHILD');
} elseif (empty($this->template->xmldata->parent) && empty($this->template->xmldata->namespace)) {
// We can't copy parent templates nor namespaced templates
ToolbarHelper::modal('copyModal', 'icon-copy', 'COM_TEMPLATES_BUTTON_COPY_TEMPLATE');
}
}
}
// Add a Template preview button
if ($this->type === 'home') {
$client = (int) $this->preview->client_id === 1 ? 'administrator/' : '';
$toolbar->linkButton('preview', 'COM_TEMPLATES_BUTTON_PREVIEW')
->url(Uri::root() . $client . 'index.php?tp=1&templateStyle=' . $this->preview->id)
->icon('icon-image')
->attributes(['target' => '_new']);
}
// Only show file manage buttons for global SuperUser
if ($isSuperUser) {
if ($this->type === 'home') {
// Add Manage folders button
ToolbarHelper::modal('folderModal', 'icon-folder icon white', 'COM_TEMPLATES_BUTTON_FOLDERS');
// Add a new file button
ToolbarHelper::modal('fileModal', 'icon-file', 'COM_TEMPLATES_BUTTON_FILE');
} else {
// Add a Rename file Button
ToolbarHelper::modal('renameModal', 'icon-sync', 'COM_TEMPLATES_BUTTON_RENAME_FILE');
// Add a Delete file Button
ToolbarHelper::modal('deleteModal', 'icon-times', 'COM_TEMPLATES_BUTTON_DELETE_FILE', 'btn-danger');
}
}
if (\count($this->updatedList) !== 0 && $this->pluginState && $this->type === 'home') {
/** @var DropdownButton $dropdown */
$dropdown = $toolbar->dropdownButton('override-group', 'COM_TEMPLATES_BUTTON_CHECK')
->toggleSplit(false)
->icon('icon-ellipsis-h')
->buttonClass('btn btn-action')
->form('updateForm')
->listCheck(true);
$childBar = $dropdown->getChildToolbar();
$childBar->publish('template.publish')
->text('COM_TEMPLATES_BUTTON_CHECK_LIST_ENTRY')
->form('updateForm')
->listCheck(true);
$childBar->unpublish('template.unpublish')
->text('COM_TEMPLATES_BUTTON_UNCHECK_LIST_ENTRY')
->form('updateForm')
->listCheck(true);
$childBar->unpublish('template.deleteOverrideHistory')
->text('COM_TEMPLATES_BUTTON_DELETE_LIST_ENTRY')
->form('updateForm')
->listCheck(true);
}
if ($this->type === 'home') {
$toolbar->cancel('template.cancel');
} else {
$toolbar->cancel('template.close', 'COM_TEMPLATES_BUTTON_CLOSE_FILE');
}
$toolbar->divider();
$toolbar->help('Templates:_Customise');
}
/**
* Method for creating the collapsible tree.
*
* @param array $array The value of the present node for recursion
*
* @return string
*
* @note Uses recursion
* @since 3.2
*/
protected function directoryTree($array)
{
$temp = $this->files;
$this->files = $array;
$txt = $this->loadTemplate('tree');
$this->files = $temp;
return $txt;
}
/**
* Method for listing the folder tree in modals.
*
* @param array $array The value of the present node for recursion
*
* @return string
*
* @note Uses recursion
* @since 3.2
*/
protected function folderTree($array)
{
$temp = $this->files;
$this->files = $array;
$txt = $this->loadTemplate('folders');
$this->files = $temp;
return $txt;
}
/**
* Method for creating the collapsible tree.
*
* @param array $array The value of the present node for recursion
*
* @return string
*
* @note Uses recursion
* @since 4.1.0
*/
protected function mediaTree($array)
{
$temp = $this->mediaFiles;
$this->mediaFiles = $array;
$txt = $this->loadTemplate('tree_media');
$this->mediaFiles = $temp;
return $txt;
}
/**
* Method for listing the folder tree in modals.
*
* @param array $array The value of the present node for recursion
*
* @return string
*
* @note Uses recursion
* @since 4.1.0
*/
protected function mediaFolderTree($array)
{
$temp = $this->mediaFiles;
$this->mediaFiles = $array;
$txt = $this->loadTemplate('media_folders');
$this->mediaFiles = $temp;
return $txt;
}
}

View File

@ -0,0 +1,160 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2008 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Component\Templates\Administrator\View\Templates;
use Joomla\CMS\Component\ComponentHelper;
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\Plugin\PluginHelper;
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 templates.
*
* @since 1.6
*/
class HtmlView extends BaseHtmlView
{
/**
* The list of templates
*
* @var array
* @since 1.6
*/
protected $items;
/**
* The pagination object
*
* @var object
* @since 1.6
*/
protected $pagination;
/**
* The model state
*
* @var object
* @since 1.6
*/
protected $state;
/**
* @var string
* @since 3.2
*/
protected $file;
/**
* Form object for search filters
*
* @var \Joomla\CMS\Form\Form
*
* @since 4.0.0
*/
public $filterForm;
/**
* The active search filters
*
* @var array
* @since 4.0.0
*/
public $activeFilters;
/**
* Is the parameter enabled to show template positions in the frontend?
*
* @var boolean
* @since 4.0.0
*/
public $preview;
/**
* The state of installer override plugin.
*
* @var array
*
* @since 4.0.0
*/
protected $pluginState;
/**
* 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
*
* @since 1.6
*/
public function display($tpl = null)
{
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->total = $this->get('Total');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
$this->preview = ComponentHelper::getParams('com_templates')->get('template_positions_display');
$this->file = base64_encode('home');
$this->pluginState = PluginHelper::isEnabled('installer', 'override');
// Check for errors.
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
*
* @since 1.6
*/
protected function addToolbar()
{
$canDo = ContentHelper::getActions('com_templates');
$clientId = (int) $this->get('State')->get('client_id');
$toolbar = Toolbar::getInstance();
// Add a shortcut to the styles list view.
$toolbar->linkButton('', 'COM_TEMPLATES_MANAGER_STYLES_BUTTON')
->url('index.php?option=com_templates&view=styles&client_id=' . $clientId)
->icon('icon-brush thememanager');
// Set the title.
if ($clientId === 1) {
ToolbarHelper::title(Text::_('COM_TEMPLATES_MANAGER_TEMPLATES_ADMIN'), 'icon-code thememanager');
} else {
ToolbarHelper::title(Text::_('COM_TEMPLATES_MANAGER_TEMPLATES_SITE'), 'icon-code thememanager');
}
if ($canDo->get('core.admin') || $canDo->get('core.options')) {
$toolbar->preferences('com_templates');
$toolbar->divider();
}
$toolbar->help('Templates:_Templates');
}
}

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="component" method="upgrade">
<name>com_templates</name>
<author>Joomla! Project</author>
<creationDate>2006-04</creationDate>
<copyright>(C) 2006 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.0.0</version>
<description>COM_TEMPLATES_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Component\Templates</namespace>
<media destination="com_templates" folder="media">
<folder>css</folder>
<folder>js</folder>
</media>
<administration>
<files folder="admin">
<filename>access.xml</filename>
<filename>config.xml</filename>
<filename>templates.xml</filename>
<folder>forms</folder>
<folder>helpers</folder>
<folder>services</folder>
<folder>src</folder>
<folder>tmpl</folder>
</files>
<languages folder="admin">
<language tag="en-GB">language/en-GB/com_templates.ini</language>
<language tag="en-GB">language/en-GB/com_templates.sys.ini</language>
</languages>
</administration>
</extension>

View File

@ -0,0 +1,116 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 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;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('keepalive')
->useScript('form.validate');
$this->useCoreUI = true;
$user = $this->getCurrentUser();
?>
<form action="<?php echo Route::_('index.php?option=com_templates&layout=edit&id=' . (int) $this->item->id); ?>" method="post" name="adminForm" id="style-form" aria-label="<?php echo Text::_('COM_TEMPLATES_STYLE_FORM_EDIT'); ?>" class="form-validate">
<?php echo LayoutHelper::render('joomla.edit.title_alias', $this); ?>
<div class="main-card">
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'details', 'recall' => true, 'breakpoint' => 768]); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'details', Text::_('JDETAILS')); ?>
<div class="row">
<div class="col-lg-9">
<h2>
<?php echo Text::_($this->item->template); ?>
</h2>
<div class="info-labels">
<span class="badge bg-secondary">
<?php echo $this->item->client_id == 0 ? Text::_('JSITE') : Text::_('JADMINISTRATOR'); ?>
</span>
</div>
<div>
<p><?php echo Text::_($this->item->xml->description); ?></p>
<?php
$this->fieldset = 'description';
$description = LayoutHelper::render('joomla.edit.fieldset', $this);
?>
<?php if ($description) : ?>
<p class="readmore">
<a href="#" onclick="document.querySelector('#tab-description').click();">
<?php echo Text::_('JGLOBAL_SHOW_FULL_DESCRIPTION'); ?>
</a>
</p>
<?php endif; ?>
</div>
<?php
$this->fieldset = 'basic';
$html = LayoutHelper::render('joomla.edit.fieldset', $this);
echo $html ? '<hr>' . $html : '';
?>
</div>
<div class="col-lg-3">
<?php
// Set main fields.
$this->fields = [
'home',
'client_id',
'template'
];
?>
<?php echo LayoutHelper::render('joomla.edit.global', $this); ?>
<?php echo $this->form->renderField('inheritable'); ?>
<?php echo $this->form->renderField('parent'); ?>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php if ($description) : ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'description', Text::_('JGLOBAL_FIELDSET_DESCRIPTION')); ?>
<fieldset id="fieldset-description" class="options-form">
<legend><?php echo Text::_('JGLOBAL_FIELDSET_DESCRIPTION'); ?></legend>
<div>
<?php echo $description; ?>
</div>
</fieldset>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php endif; ?>
<?php
$this->fieldsets = [];
$this->ignore_fieldsets = ['basic', 'description'];
echo LayoutHelper::render('joomla.edit.params', $this);
?>
<?php if ($user->authorise('core.edit', 'com_menus') && $this->item->client_id == 0 && $this->canDo->get('core.edit.state')) : ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'assignment', Text::_('COM_TEMPLATES_MENUS_ASSIGNMENT')); ?>
<fieldset id="fieldset-assignment" class="options-form">
<legend><?php echo Text::_('COM_TEMPLATES_MENUS_ASSIGNMENT'); ?></legend>
<div>
<?php echo $this->loadTemplate('assignment'); ?>
</div>
</fieldset>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php endif; ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</form>

View File

@ -0,0 +1,61 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2010 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\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\Component\Menus\Administrator\Helper\MenusHelper;
// Initialise related data.
$menuTypes = MenusHelper::getMenuLinks();
$user = $this->getCurrentUser();
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('com_templates.admin-template-toggle-assignment');
?>
<label id="jform_menuselect-lbl" for="jform_menuselect"><?php echo Text::_('JGLOBAL_MENU_SELECTION'); ?></label>
<div class="btn-toolbar">
<button class="btn btn-sm btn-secondary jform-rightbtn" type="button" onclick="Joomla.toggleAll()">
<span class="icon-square" aria-hidden="true"></span> <?php echo Text::_('JGLOBAL_SELECTION_INVERT_ALL'); ?>
</button>
</div>
<div id="menu-assignment" class="menu-assignment">
<ul class="menu-links">
<?php foreach ($menuTypes as &$type) : ?>
<li>
<div class="menu-links-block">
<button class="btn btn-sm btn-secondary jform-rightbtn mb-2" type="button" onclick='Joomla.toggleMenutype("<?php echo $type->menutype; ?>")'>
<span class="icon-square" aria-hidden="true"></span> <?php echo Text::_('JGLOBAL_SELECTION_INVERT'); ?>
</button>
<h5><?php echo $type->title ?: $type->menutype; ?></h5>
<?php foreach ($type->links as $link) : ?>
<label class="checkbox small" for="link<?php echo (int) $link->value; ?>" >
<input type="checkbox" name="jform[assigned][]" value="<?php echo (int) $link->value; ?>" id="link<?php echo (int) $link->value; ?>"<?php if ($link->template_style_id == $this->item->id) :
?> checked="checked"<?php
endif; ?><?php if ($link->checked_out && $link->checked_out != $user->id) :
?> disabled="disabled"<?php
else :
?> class="form-check-input chk-menulink menutype-<?php echo $type->menutype; ?>"<?php
endif; ?> />
<?php echo LayoutHelper::render('joomla.html.treeprefix', ['level' => $link->level]) . $link->text; ?>
</label>
<?php endforeach; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>

View File

@ -0,0 +1,153 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2009 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\Session\Session;
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('table.columns')
->useScript('multiselect');
$user = $this->getCurrentUser();
$clientId = (int) $this->state->get('client_id', 0);
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
?>
<form action="<?php echo Route::_('index.php?option=com_templates&view=styles'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<div class="col-md-12">
<div id="j-main-container" class="j-main-container">
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this, 'options' => ['selectorFieldName' => 'client_id']]); ?>
<?php if ($this->total > 0) : ?>
<table class="table" id="styleList">
<caption class="visually-hidden">
<?php echo Text::_('COM_TEMPLATES_STYLES_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<thead>
<tr>
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<th scope="col">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_TEMPLATES_HEADING_STYLE', 'a.title', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-5 text-center">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_PREVIEW'); ?>
</th>
<th scope="col" class="w-12 text-center">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_TEMPLATES_HEADING_DEFAULT', 'a.home', $listDirn, $listOrder); ?>
</th>
<?php if ($clientId === 0) : ?>
<th scope="col" class="w-12 d-none d-md-table-cell">
<?php echo Text::_('COM_TEMPLATES_HEADING_PAGES'); ?>
</th>
<?php endif; ?>
<th scope="col" class="w-12 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_TEMPLATES_HEADING_TEMPLATE', 'a.template', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-5 d-none d-md-table-cell">
<?php echo HTMLHelper::_('searchtools.sort', 'JGRID_HEADING_ID', 'a.id', $listDirn, $listOrder); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->items as $i => $item) :
$canCreate = $user->authorise('core.create', 'com_templates');
$canEdit = $user->authorise('core.edit', 'com_templates');
$canChange = $user->authorise('core.edit.state', 'com_templates');
?>
<tr class="row<?php echo $i % 2; ?>">
<td class="w-1 text-center">
<?php echo HTMLHelper::_('grid.id', $i, $item->id, false, 'cid', 'cb', $item->title); ?>
</td>
<th scope="row">
<?php if ($canEdit) : ?>
<a href="<?php echo Route::_('index.php?option=com_templates&task=style.edit&id=' . (int) $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; ?>
</th>
<td class="text-center">
<?php if ($this->preview) : ?>
<?php $client = (int) $item->client_id === 1 ? 'administrator' : 'site'; ?>
<!-- external link icon generated by css -->
<a href="<?php echo Route::link($client, 'index.php?tp=1&templateStyle=' . (int) $item->id); ?>" target="_blank" class="jgrid" aria-labelledby="preview-<?php echo (int) $item->id; ?>"></a>
<div role="tooltip" id="preview-<?php echo (int) $item->id; ?>"><?php echo Text::sprintf('COM_TEMPLATES_TEMPLATE_NEW_PREVIEW', $item->title); ?></div>
<?php else : ?>
<span class="icon-eye-slash" aria-labelledby="nopreview-<?php echo (int) $item->id; ?>" aria-hidden="true"></span>
<div role="tooltip" id="nopreview-<?php echo (int) $item->id; ?>"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_NO_PREVIEW'); ?></div>
<?php endif; ?>
</td>
<td class="text-center">
<?php if ($item->home == '0' || $item->home == '1') : ?>
<?php echo HTMLHelper::_('jgrid.isdefault', $item->home != '0', $i, 'styles.', $canChange && $item->home != '1'); ?>
<?php elseif ($canChange) :?>
<a href="<?php echo Route::_('index.php?option=com_templates&task=styles.unsetDefault&cid[]=' . $item->id . '&' . Session::getFormToken() . '=1'); ?>">
<?php if ($item->image) : ?>
<?php echo HTMLHelper::_('image', 'mod_languages/' . $item->image . '.gif', $item->language_title, ['title' => Text::sprintf('COM_TEMPLATES_GRID_UNSET_LANGUAGE', $item->language_title)], true); ?>
<?php else : ?>
<span class="badge bg-secondary" title="<?php echo Text::sprintf('COM_TEMPLATES_GRID_UNSET_LANGUAGE', $item->language_title); ?>"><?php echo $item->home; ?></span>
<?php endif; ?>
</a>
<?php else : ?>
<?php if ($item->image) : ?>
<?php echo HTMLHelper::_('image', 'mod_languages/' . $item->image . '.gif', $item->language_title, ['title' => $item->language_title], true); ?>
<?php else : ?>
<span class="badge bg-secondary" title="<?php echo $item->language_title; ?>"><?php echo $item->home; ?></span>
<?php endif; ?>
<?php endif; ?>
</td>
<?php if ($clientId === 0) : ?>
<td class="small d-none d-md-table-cell">
<?php if ($item->home == '1') : ?>
<?php echo Text::_('COM_TEMPLATES_STYLES_PAGES_ALL'); ?>
<?php elseif ($item->home != '0' && $item->home != '1') : ?>
<?php echo Text::sprintf('COM_TEMPLATES_STYLES_PAGES_ALL_LANGUAGE', $this->escape($item->language_title)); ?>
<?php elseif ($item->assigned > 0) : ?>
<?php echo Text::plural('COM_TEMPLATES_STYLES_PAGES_SELECTED', $this->escape($item->assigned)); ?>
<?php else : ?>
<?php echo Text::_('COM_TEMPLATES_STYLES_PAGES_NONE'); ?>
<?php endif; ?>
</td>
<?php endif; ?>
<td class="small d-none d-md-table-cell">
<a href="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . (int) $item->e_id); ?>">
<?php echo ucfirst($this->escape($item->template)); ?>
</a>
</td>
<td class="d-none d-md-table-cell">
<?php echo (int) $item->id; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php // load the pagination. ?>
<?php 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>
</div>
</div>
</form>

View File

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

View File

@ -0,0 +1,475 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2008 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 Jfcherng\Diff\DiffHelper;
use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
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;
HTMLHelper::_('behavior.multiselect', 'updateForm');
HTMLHelper::_('bootstrap.modal');
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$input = Factory::getApplication()->getInput();
// Enable assets
$wa->useScript('form.validate')
->useScript('keepalive')
->useScript('com_templates.admin-template-toggle-switch')
->useScript('com_templates.admin-templates')
->useStyle('com_templates.admin-templates');
// No access if not global SuperUser
if (!$this->getCurrentUser()->authorise('core.admin')) {
Factory::getApplication()->enqueueMessage(Text::_('JERROR_ALERTNOAUTHOR'), 'danger');
}
if ($this->type == 'image') {
$wa->usePreset('cropperjs');
}
if ($this->type == 'font') {
$wa->addInlineStyle("
@font-face {
font-family: previewFont;
src: url('" . $this->font['address'] . "')
}
.font-preview {
font-family: previewFont !important;
}
");
}
?>
<div class="main-card">
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'editor', 'recall' => true, 'breakpoint' => 768]); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'editor', Text::_('COM_TEMPLATES_TAB_EDITOR')); ?>
<div class="row mt-2">
<div class="col-md-8" id="conditional-section">
<?php if ($this->type != 'home') : ?>
<p class="lead"><?php echo Text::sprintf('COM_TEMPLATES_TEMPLATE_FILENAME', '&#x200E;' . ($input->get('isMedia', 0) ? '/media/templates/' . ($this->template->client_id === 0 ? 'site' : 'administrator') . '/' . $this->template->element . str_replace('//', '/', base64_decode($this->file)) : '/' . ($this->template->client_id === 0 ? '' : 'administrator/') . 'templates/' . $this->template->element . str_replace('//', '/', base64_decode($this->file))), $this->template->element); ?></p>
<p class="lead path hidden"><?php echo $this->source->filename; ?></p>
<?php endif; ?>
</div>
<?php if ($this->type == 'file' && !empty($this->source->coreFile)) : ?>
<div class="col-md-4 text-end">
<div id="toggle-buttons">
<?php echo $this->form->renderField('show_core'); ?>
<?php echo $this->form->renderField('show_diff'); ?>
</div>
</div>
<?php endif; ?>
</div>
<div class="row mt-2">
<div id="treeholder" class="col-md-3 tree-holder">
<div class="mt-2 mb-2">
<ul class="directory-tree treeselect">
<li class="folder-select">
<a class="folder-url" data-id="" href="">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php echo ($this->template->client_id === 0 ? '/' : '/administrator/') . 'templates/' . $this->template->element; ?>
</a>
<?php echo $this->loadTemplate('tree'); ?>
</li>
</ul>
<?php if (count($this->mediaFiles)) : ?>
<ul class="directory-tree treeselect">
<li class="folder-select">
<a class="folder-url" data-id="" href="">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php echo '/media/templates/' . ($this->template->client_id === 0 ? 'site/' : 'administrator/') . $this->template->element; ?>
</a>
<?php echo $this->loadTemplate('tree_media'); ?>
</li>
</ul>
<?php endif; ?>
</div>
</div>
<div class="col-md-9">
<fieldset class="options-form">
<?php if ($this->type == 'home') : ?>
<legend><?php echo Text::_('COM_TEMPLATES_HOME_HEADING'); ?></legend>
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post" name="adminForm" id="adminForm">
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
<p><?php echo Text::_('COM_TEMPLATES_HOME_TEXT'); ?></p>
<p>
<a href="https://docs.joomla.org/Special:MyLanguage/J4.x:Template_Overrides" target="_blank" rel="noopener" class="btn btn-primary btn-lg">
<?php echo Text::_('COM_TEMPLATES_HOME_BUTTON'); ?>
</a>
</p>
</form>
<?php elseif ($this->type == 'file') : ?>
<div class="row">
<div class="col-md-12" id="override-pane">
<?php $overrideCheck = explode(DIRECTORY_SEPARATOR, $this->source->filename); ?>
<?php if (!empty($this->source->coreFile)) : ?>
<h2><?php echo Text::_('COM_TEMPLATES_FILE_OVERRIDE_PANE'); ?></h2>
<?php endif; ?>
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post" name="adminForm" id="adminForm">
<input type="hidden" name="isMedia" value="<?php echo $input->get('isMedia', 0); ?>">
<div class="editor-border">
<?php echo $this->form->getInput('source'); ?>
</div>
<input type="hidden" name="task" value="" />
<?php echo HTMLHelper::_('form.token'); ?>
<?php echo $this->form->getInput('extension_id'); ?>
<?php echo $this->form->getInput('filename'); ?>
</form>
</div>
<?php if (!empty($this->source->coreFile)) : ?>
<div class="col-md-12" id="core-pane">
<h2><?php echo Text::_('COM_TEMPLATES_FILE_CORE_PANE'); ?></h2>
<div class="editor-border">
<?php echo $this->form->getInput('core'); ?>
</div>
</div>
<?php
$difference = DiffHelper::calculateFiles(
$this->source->coreFile,
$this->source->filePath,
ComponentHelper::getParams('com_templates')->get('difference', 'SideBySide'),
[
'context' => 1,
'ignoreLineEnding' => true,
],
[
'language' => [
'old_version' => Text::_('COM_TEMPLATES_DIFF_CORE'),
'new_version' => Text::_('COM_TEMPLATES_DIFF_OVERRIDE'),
'differences' => Text::_('COM_TEMPLATES_DIFF_DIFFERENCES'),
],
'resultForIdenticals' => Text::_('COM_TEMPLATES_DIFF_IDENTICAL'),
'detailLevel' => 'word',
'spaceToHtmlTag' => true,
'wrapperClasses' => ['diff-wrapper', 'columns-order-ignore'],
]
);
?>
<div class="col-md-12" id="diff-main">
<h2><?php echo Text::_('COM_TEMPLATES_FILE_COMPARE_PANE'); ?></h2>
<div class="diff-pane">
<div id="diff"><?php echo $difference; ?></div>
</div>
</div>
<?php endif; ?>
</div>
<?php elseif ($this->type == 'archive') : ?>
<legend><?php echo Text::_('COM_TEMPLATES_FILE_CONTENT_PREVIEW'); ?></legend>
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post" name="adminForm" id="adminForm">
<ul class="nav flex-column well">
<?php foreach ($this->archive as $file) : ?>
<li>
<?php if (substr($file, -1) === DIRECTORY_SEPARATOR) : ?>
<span class="icon-folder icon-fw" aria-hidden="true"></span>&nbsp;<?php echo $file; ?>
<?php endif; ?>
<?php if (substr($file, -1) != DIRECTORY_SEPARATOR) : ?>
<span class="icon-file icon-fw" aria-hidden="true"></span>&nbsp;<?php echo $file; ?>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php elseif ($this->type == 'image') : ?>
<legend><?php echo $this->escape(basename($this->image['address'])); ?></legend>
<img id="image-crop" src="<?php echo $this->image['address'] . '?' . time(); ?>" style="max-width: 100%">
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post" name="adminForm" id="adminForm">
<fieldset class="adminform">
<input type="hidden" id="x" name="x">
<input type="hidden" id="y" name="y">
<input type="hidden" id="h" name="h">
<input type="hidden" id="w" name="w">
<input type="hidden" id="imageWidth" value="<?php echo $this->image['width']; ?>">
<input type="hidden" id="imageHeight" value="<?php echo $this->image['height']; ?>">
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</fieldset>
</form>
<?php elseif ($this->type == 'font') : ?>
<div class="font-preview">
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post" name="adminForm" id="adminForm">
<fieldset class="adminform">
<h1>H1. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</h1>
<h2>H2. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</h2>
<h3>H3. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</h3>
<h4>H4. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</h4>
<h5>H5. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</h5>
<h6>H6. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</h6>
<p><strong>Bold. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</strong></p>
<p><em>Italics. Quickly gaze at Joomla! views from HTML, CSS, JavaScript and XML</em></p>
<p>Unordered List</p>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item<br>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item<br>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</li>
</ul>
</li>
</ul>
<p class="lead">Ordered List</p>
<ol>
<li>Item</li>
<li>Item</li>
<li>Item<br>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item<br>
<ul>
<li>Item</li>
<li>Item</li>
<li>Item</li>
</ul>
</li>
</ul>
</li>
</ol>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</fieldset>
</form>
</div>
<?php endif; ?>
</fieldset>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'overrides', Text::_('COM_TEMPLATES_TAB_OVERRIDES')); ?>
<div class="row mt-2">
<div class="col-md-3">
<fieldset class="options-form">
<legend><?php echo Text::_('COM_TEMPLATES_OVERRIDES_MODULES'); ?></legend>
<ul class="list-unstyled">
<?php $token = Session::getFormToken() . '=' . 1; ?>
<?php foreach ($this->overridesList['modules'] as $module) : ?>
<li>
<?php
$overrideLinkUrl = 'index.php?option=com_templates&view=template&task=template.overrides&folder=' . $module->path
. '&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0) . '&' . $token;
?>
<a href="<?php echo Route::_($overrideLinkUrl); ?>">
<span class="icon-copy" aria-hidden="true"></span>&nbsp;<?php echo $module->name; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</fieldset>
</div>
<div class="col-md-3">
<fieldset class="options-form">
<legend><?php echo Text::_('COM_TEMPLATES_OVERRIDES_COMPONENTS'); ?></legend>
<ul class="list-unstyled">
<?php $token = Session::getFormToken() . '=' . 1; ?>
<?php foreach ($this->overridesList['components'] as $key => $value) : ?>
<li class="component-folder">
<a href="#" class="component-folder-url">
<span class="icon-folder icon-fw" aria-hidden="true"></span>&nbsp;<?php echo $key; ?>
</a>
<ul class="list-unstyled">
<?php foreach ($value as $view) : ?>
<li>
<?php
$overrideLinkUrl = 'index.php?option=com_templates&view=template&task=template.overrides&folder=' . $view->path
. '&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0) . '&' . $token;
?>
<a class="component-file-url" href="<?php echo Route::_($overrideLinkUrl); ?>">
<span class="icon-copy" aria-hidden="true"></span>&nbsp;<?php echo $view->name; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</li>
<?php endforeach; ?>
</ul>
</fieldset>
</div>
<div class="col-md-3">
<fieldset class="options-form">
<legend><?php echo Text::_('COM_TEMPLATES_OVERRIDES_PLUGINS'); ?></legend>
<ul class="list-unstyled">
<?php $token = Session::getFormToken() . '=' . 1; ?>
<?php foreach ($this->overridesList['plugins'] as $key => $group) : ?>
<li class="plugin-folder">
<a href="#" class="plugin-folder-url">
<span class="icon-folder icon-fw" aria-hidden="true"></span>&nbsp;<?php echo $key; ?>
</a>
<ul class="list-unstyled">
<?php foreach ($group as $plugin) : ?>
<li>
<?php
$overrideLinkUrl = 'index.php?option=com_templates&view=template&task=template.overrides&folder=' . $plugin->path
. '&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0) . '&' . $token;
?>
<a class="plugin-file-url" href="<?php echo Route::_($overrideLinkUrl); ?>">
<span class="icon-copy" aria-hidden="true"></span> <?php echo $plugin->name; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</li>
<?php endforeach; ?>
</ul>
</fieldset>
</div>
<div class="col-md-3">
<fieldset class="options-form">
<legend><?php echo Text::_('COM_TEMPLATES_OVERRIDES_LAYOUTS'); ?></legend>
<ul class="list-unstyled">
<?php $token = Session::getFormToken() . '=' . 1; ?>
<?php foreach ($this->overridesList['layouts'] as $key => $value) : ?>
<li class="layout-folder">
<a href="#" class="layout-folder-url">
<span class="icon-folder icon-fw" aria-hidden="true"></span>&nbsp;<?php echo $key; ?>
</a>
<ul class="list-unstyled">
<?php foreach ($value as $layout) : ?>
<li>
<?php
$overrideLinkUrl = 'index.php?option=com_templates&view=template&task=template.overrides&folder=' . $layout->path
. '&id=' . $input->getInt('id') . '&file=' . $this->file . '&' . $token . '&isMedia=' . $input->get('isMedia', 0);
?>
<a href="<?php echo Route::_($overrideLinkUrl); ?>">
<span class="icon-copy" aria-hidden="true"></span>&nbsp;<?php echo $layout->name; ?>
</a>
</li>
<?php endforeach; ?>
</ul>
</li>
<?php endforeach; ?>
</ul>
</fieldset>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php if ($this->pluginState) : ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'files', Text::_('COM_TEMPLATES_TAB_UPDATED_FILES')); ?>
<?php echo $this->loadTemplate('updated_files'); ?>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php endif; ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'description', Text::_('COM_TEMPLATES_TAB_DESCRIPTION')); ?>
<div class="row mt-2">
<div class="col-12">
<?php echo $this->loadTemplate('description'); ?>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
<?php // Collapse Modal
$taskName = isset($this->template->xmldata->inheritable) && (string) $this->template->xmldata->inheritable === '1' ? 'child' : 'copy';
$copyModalData = [
'selector' => $taskName . 'Modal',
'params' => [
'title' => Text::_('COM_TEMPLATES_TEMPLATE_' . strtoupper($taskName)),
'footer' => $this->loadTemplate('modal_' . $taskName . '_footer')
],
'body' => $this->loadTemplate('modal_' . $taskName . '_body')
];
?>
<form action="<?php echo Route::_('index.php?option=com_templates&task=template.' . $taskName . '&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" method="post">
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $copyModalData); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php if ($this->type != 'home') : ?>
<?php // Rename Modal
$renameModalData = [
'selector' => 'renameModal',
'params' => [
'title' => Text::sprintf('COM_TEMPLATES_RENAME_FILE', str_replace('//', '/', $this->fileName)),
'footer' => $this->loadTemplate('modal_rename_footer')
],
'body' => $this->loadTemplate('modal_rename_body')
];
?>
<form action="<?php echo Route::_('index.php?option=com_templates&task=template.renameFile&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post">
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $renameModalData); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php endif; ?>
<?php if ($this->type != 'home') : ?>
<?php // Delete Modal
$deleteModalData = [
'selector' => 'deleteModal',
'params' => [
'title' => Text::_('COM_TEMPLATES_ARE_YOU_SURE'),
'footer' => $this->loadTemplate('modal_delete_footer')
],
'body' => $this->loadTemplate('modal_delete_body')
];
?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $deleteModalData); ?>
<?php endif; ?>
<?php // File Modal
$fileModalData = [
'selector' => 'fileModal',
'params' => [
'title' => Text::_('COM_TEMPLATES_NEW_FILE_HEADER'),
'footer' => $this->loadTemplate('modal_file_footer'),
'height' => '400px',
'width' => '800px',
'bodyHeight' => 70,
'modalWidth' => 80,
],
'body' => $this->loadTemplate('modal_file_body')
];
?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $fileModalData); ?>
<?php // Folder Modal
$folderModalData = [
'selector' => 'folderModal',
'params' => [
'title' => Text::_('COM_TEMPLATES_MANAGE_FOLDERS'),
'footer' => $this->loadTemplate('modal_folder_footer'),
'height' => '400px',
'width' => '800px',
'bodyHeight' => 70,
'modalWidth' => 80,
],
'body' => $this->loadTemplate('modal_folder_body')
];
?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $folderModalData); ?>
<?php if ($this->type == 'image') : ?>
<?php // Resize Modal
$resizeModalData = [
'selector' => 'resizeModal',
'params' => [
'title' => Text::_('COM_TEMPLATES_RESIZE_IMAGE'),
'footer' => $this->loadTemplate('modal_resize_footer')
],
'body' => $this->loadTemplate('modal_resize_body')
];
?>
<form action="<?php echo Route::_('index.php?option=com_templates&task=template.resizeImage&id=' . $input->getInt('id') . '&file=' . $this->file . '&isMedia=' . $input->get('isMedia', 0)); ?>" method="post">
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $resizeModalData); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php endif; ?>
</div>

View File

@ -0,0 +1,29 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2013 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\Application\ApplicationHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\Component\Templates\Administrator\Helper\TemplatesHelper;
?>
<div class="clearfix">
<div class="float-start me-3 text-center">
<?php echo HTMLHelper::_('templates.thumb', $this->template); ?>
<?php echo HTMLHelper::_('templates.thumbModal', $this->template); ?>
</div>
<h2><?php echo ucfirst($this->template->element); ?></h2>
<?php $client = ApplicationHelper::getClientInfo($this->template->client_id); ?>
<p><?php $this->template->xmldata = TemplatesHelper::parseXMLTemplateFile($client->path, $this->template->element); ?></p>
<p><?php echo Text::_($this->template->xmldata->get('description')); ?></p>
</div>

View File

@ -0,0 +1,29 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
ksort($this->files, SORT_STRING);
?>
<ul class="directory-tree treeselect">
<?php foreach ($this->files as $key => $value) : ?>
<?php if (is_array($value)) : ?>
<li class="folder-select">
<a class="folder-url" data-id="<?php echo base64_encode($key); ?>" href="" data-base="template">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php $explodeArray = explode('/', rtrim($key, '\\'));
echo $this->escape(end($explodeArray)); ?>
</a>
<?php echo $this->folderTree($value); ?>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>

View File

@ -0,0 +1,33 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2021 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
// Legacy is the default
if (!count($this->mediaFiles)) {
return;
}
ksort($this->mediaFiles, SORT_STRING);
?>
<ul class="directory-tree treeselect">
<?php foreach ($this->mediaFiles as $key => $value) : ?>
<?php if (is_array($value)) : ?>
<li class="folder-select">
<a class="folder-url" data-id="<?php echo base64_encode($key); ?>" href="" data-base="media">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php $explodeArray = explode('/', rtrim($key, '\\'));
echo $this->escape(end($explodeArray)); ?>
</a>
<?php echo $this->mediaFolderTree($value); ?>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>

View File

@ -0,0 +1,89 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2021 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;
Factory::getDocument()->getWebAssetManager()->usePreset('choicesjs');
// Generate a list of styles for the child creation modal
$options = [];
if (count($this->styles) > 0) {
foreach ($this->styles as $style) {
$options[] = HTMLHelper::_('select.option', $style->id, $style->title, 'value', 'text');
}
}
$fancySelectData = [
'autocomplete' => 'off',
'autofocus' => false,
'class' => '',
'description' => '',
'disabled' => false,
'group' => false,
'id' => 'style_ids',
'hidden' => false,
'hint' => '',
'label' => '',
'labelclass' => '',
'onchange' => '',
'onclick' => '',
'multiple' => true,
'pattern' => '',
'readonly' => false,
'repeat' => false,
'required' => false,
'size' => 4,
'spellcheck' => false,
'validate' => '',
'value' => '0',
'options' => $options,
'dataAttributes' => [],
'dataAttribute' => '',
'name' => 'style_ids[]',
];
?>
<div id="template-manager-copy" class="container-fluid">
<div class="mt-2">
<div class="col-md-12">
<div class="control-group">
<div class="control-label">
<label for="new_name">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_CHILD_NAME_LABEL'); ?>
</label>
</div>
<div class="controls">
<input class="form-control" type="text" id="new_name" name="new_name" required>
<small class="form-text">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_NEW_NAME_DESC'); ?>
</small>
</div>
</div>
<div class="control-group">
<div class="control-label">
<label for="style_ids">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_CHILD_STYLE_LABEL'); ?>
</label>
</div>
<div class="controls">
<?php echo LayoutHelper::render('joomla.form.field.list-fancy-select', $fancySelectData); ?>
<small class="form-text">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_NEW_STYLE_DESC'); ?>
</small>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,17 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2021 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\Language\Text;
?>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_BUTTON_TEMPLATE_CHILD'); ?></button>

View File

@ -0,0 +1,34 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<div id="template-manager-copy" class="container-fluid">
<div class="mt-2">
<div class="col-md-12">
<div class="control-group">
<div class="control-label">
<label for="new_name">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_NEW_NAME_LABEL'); ?>
</label>
</div>
<div class="controls">
<input class="form-control" type="text" id="new_name" name="new_name" required>
<small class="form-text">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_NEW_NAME_DESC'); ?>
</small>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,17 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_COPY'); ?></button>

View File

@ -0,0 +1,22 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<div id="template-manager-delete" class="container-fluid">
<div class="mt-2">
<div class="col-md-12">
<p><?php echo Text::sprintf('COM_TEMPLATES_MODAL_FILE_DELETE', str_replace('//', '/', $this->fileName)); ?></p>
</div>
</div>
</div>

View File

@ -0,0 +1,27 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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;
$input = Factory::getApplication()->getInput();
?>
<form method="post" action="">
<input type="hidden" name="option" value="com_templates">
<input type="hidden" name="task" value="template.delete">
<input type="hidden" name="id" value="<?php echo $input->getInt('id'); ?>">
<input type="hidden" name="file" value="<?php echo $this->file; ?>">
<?php echo HTMLHelper::_('form.token'); ?>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>
<button type="submit" class="btn btn-danger"><?php echo Text::_('COM_TEMPLATES_BUTTON_DELETE'); ?></button>
</form>

View File

@ -0,0 +1,105 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Router\Route;
use Joomla\CMS\Utility\Utility;
$input = Factory::getApplication()->getInput();
?>
<div id="#template-manager-file" class="container-fluid">
<div class="mt-2 p-2">
<div class="row">
<div class="col-md-4">
<div class="tree-holder">
<ul class="directory-tree treeselect root">
<li class="folder-select">
<a class="folder-url" data-id="" href="" data-base="template">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php echo ($this->template->client_id === 0 ? '/' : '/administrator/') . 'templates/' . $this->template->element; ?>
</a>
<?php echo $this->loadTemplate('folders'); ?>
</li>
</ul>
<?php if (count($this->mediaFiles)) : ?>
<ul class="directory-tree treeselect">
<li class="folder-select">
<a class="folder-url" data-id="" href="" data-base="media">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php echo '/media/templates/' . ($this->template->client_id === 0 ? 'site/' : 'administrator/') . $this->template->element; ?>
</a>
<?php echo $this->loadTemplate('media_folders'); ?>
</li>
</ul>
<?php endif; ?>
</div>
</div>
<div class="col-md-8">
<form method="post" action="<?php echo Route::_('index.php?option=com_templates&task=template.createFile&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" class="mb-4">
<div class="form-group">
<label for="file_name"><?php echo Text::_('COM_TEMPLATES_FILE_NAME'); ?></label>
<input type="text" name="name" id="file_name" class="form-control" required>
</div>
<div class="form-group">
<label for="type"><?php echo Text::_('COM_TEMPLATES_NEW_FILE_TYPE'); ?></label>
<select class="form-select" data-chosen="true" name="type" id="type" required>
<option value="">- <?php echo Text::_('COM_TEMPLATES_NEW_FILE_SELECT'); ?> -</option>
<option value="css">.css</option>
<option value="php">.php</option>
<option value="js">.js</option>
<option value="xml">.xml</option>
<option value="ini">.ini</option>
<option value="less">.less</option>
<option value="sass">.sass</option>
<option value="scss">.scss</option>
<option value="txt">.txt</option>
</select>
</div>
<input type="hidden" class="address" name="address">
<input type="hidden" name="isMedia" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_BUTTON_CREATE'); ?></button>
</form>
<hr class="mb-4">
<form method="post" action="<?php echo Route::_('index.php?option=com_templates&task=template.uploadFile&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" enctype="multipart/form-data" class="mb-4">
<input type="hidden" class="address" name="address">
<input type="hidden" name="isMedia" value="0">
<div class="input-group">
<input type="file" name="files" aria-labelledby="upload" class="form-control" required>
<?php echo HTMLHelper::_('form.token'); ?>
<button type="submit" class="btn btn-primary" id="upload"><?php echo Text::_('COM_TEMPLATES_BUTTON_UPLOAD'); ?></button>
</div>
<?php $cMax = $this->state->get('params')->get('upload_limit'); ?>
<?php $maxSize = HTMLHelper::_('number.bytes', Utility::getMaxUploadSize($cMax . 'MB')); ?>
<span class="mt-2"><?php echo Text::sprintf('JGLOBAL_MAXIMUM_UPLOAD_SIZE_LIMIT', '&#x200E;' . $maxSize); ?></span>
</form>
<?php if ($this->type != 'home') : ?>
<hr class="mb-4">
<form method="post" action="<?php echo Route::_('index.php?option=com_templates&task=template.copyFile&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" enctype="multipart/form-data" class="mb-4">
<div class="form-group">
<input type="hidden" class="address" name="address">
<input type="hidden" name="isMedia" value="0">
<label for="new_name">
<?php echo Text::_('COM_TEMPLATES_FILE_NEW_NAME_LABEL') ?>
</label>
<input class="form-control" type="text" id="new_name" name="new_name" required>
<?php echo HTMLHelper::_('form.token'); ?>
</div>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_BUTTON_COPY_FILE'); ?></button>
</form>
<?php endif; ?>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>

View File

@ -0,0 +1,61 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Router\Route;
$input = Factory::getApplication()->getInput();
?>
<div id="#template-manager-folder" class="container-fluid">
<div class="mt-2 p-2">
<div class="row">
<div class="col-md-4">
<div class="tree-holder">
<ul class="directory-tree treeselect root">
<li class="folder-select">
<a class="folder-url" data-id="" href="" data-base="template">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php echo ($this->template->client_id === 0 ? '/' : '/administrator/') . 'templates/' . $this->template->element; ?>
</a>
<?php echo $this->loadTemplate('folders'); ?>
</li>
</ul>
<?php if (count($this->mediaFiles)) : ?>
<ul class="directory-tree treeselect">
<li class="folder-select">
<a class="folder-url" data-id="" href="" data-base="media">
<span class="icon-folder icon-fw" aria-hidden="true"></span>
<?php echo '/media/templates/' . ($this->template->client_id === 0 ? 'site/' : 'administrator/') . $this->template->element; ?>
</a>
<?php echo $this->loadTemplate('media_folders'); ?>
</li>
</ul>
<?php endif; ?>
</div>
</div>
<div class="col-md-8">
<form method="post" action="<?php echo Route::_('index.php?option=com_templates&task=template.createFolder&id=' . $input->getInt('id') . '&file=' . $this->file); ?>">
<div class="form-group">
<label for="folder_name"><?php echo Text::_('COM_TEMPLATES_FOLDER_NAME'); ?></label>
<input type="text" name="name" id="folder_name" class="form-control" required>
<input type="hidden" class="address" name="address">
<input type="hidden" name="isMedia" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_BUTTON_CREATE'); ?></button>
</form>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,28 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Router\Route;
$input = Factory::getApplication()->getInput();
?>
<form id="deleteFolder" method="post" action="<?php echo Route::_('index.php?option=com_templates&task=template.deleteFolder&id=' . $input->getInt('id') . '&file=' . $this->file); ?>">
<fieldset>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>
<input type="hidden" class="address" name="address">
<input type="hidden" name="isMedia" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
<button type="submit" class="btn btn-danger"><?php echo Text::_('COM_TEMPLATES_BUTTON_DELETE'); ?></button>
</fieldset>
</form>

View File

@ -0,0 +1,35 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Filesystem\File;
use Joomla\CMS\Language\Text;
?>
<div id="template-manager-rename" class="container-fluid">
<div class="mt-2">
<div class="col-md-12">
<div class="control-group">
<div class="control-label">
<label for="new_name">
<?php echo Text::_('COM_TEMPLATES_NEW_FILE_NAME')?>
</label>
</div>
<div class="controls">
<div class="input-group">
<input class="form-control" type="text" name="new_name" id="new_name" required>
<span class="input-group-text">.<?php echo File::getExt($this->fileName); ?></span>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,17 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_BUTTON_RENAME'); ?></button>

View File

@ -0,0 +1,41 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<div id="template-manager-resize" class="container-fluid">
<div class="mt-2">
<div class="col-md-12">
<div class="control-group">
<div class="control-label">
<label for="height">
<?php echo Text::_('COM_TEMPLATES_IMAGE_HEIGHT')?>
</label>
</div>
<div class="controls">
<input class="form-control" type="number" name="height" id="height" placeholder="<?php echo $this->image['height']; ?> px" required>
</div>
</div>
<div class="control-group">
<div class="control-label">
<label for="width">
<?php echo Text::_('COM_TEMPLATES_IMAGE_WIDTH')?>
</label>
</div>
<div class="controls">
<input class="form-control" type="number" name="width" id="width" placeholder="<?php echo $this->image['width']; ?> px" required>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,17 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2016 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\Language\Text;
?>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?php echo Text::_('COM_TEMPLATES_TEMPLATE_CLOSE'); ?></button>
<button type="submit" class="btn btn-primary"><?php echo Text::_('COM_TEMPLATES_BUTTON_RESIZE'); ?></button>

View File

@ -0,0 +1,61 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2013 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\Router\Route;
ksort($this->files, SORT_NATURAL);
?>
<ul class="directory-tree treeselect">
<?php foreach ($this->files as $key => $value) : ?>
<?php if (is_array($value)) : ?>
<?php
$keyArray = explode('/', rtrim($key, '\\'));
$fileArray = explode('/', $this->fileName);
$count = 0;
$keyArrayCount = count($keyArray);
if (count($fileArray) >= $keyArrayCount) {
for ($i = 0; $i < $keyArrayCount; $i++) {
if ($keyArray[$i] === $fileArray[$i]) {
$count++;
}
}
if ($count === $keyArrayCount) {
$class = 'folder show';
} else {
$class = 'folder';
}
} else {
$class = 'folder';
}
?>
<li class="<?php echo $class; ?>">
<a class="folder-url" href="">
<span class="icon-folder icon-fw" aria-hidden="true"></span>&nbsp;<?php $explodeArray = explode('/', $key);
echo $this->escape(end($explodeArray)); ?>
</a>
<?php echo $this->directoryTree($value); ?>
</li>
<?php endif; ?>
<?php if (is_object($value)) : ?>
<li>
<a class="file" href='<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $this->id . '&file=' . $value->id . '&isMedia=0'); ?>'>
<span class="icon-file-alt" aria-hidden="true"></span>&nbsp;<?php echo $this->escape($value->name); ?>
</a>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>

View File

@ -0,0 +1,66 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2021 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\Router\Route;
// Legacy is the default
if (!count($this->mediaFiles)) {
return;
}
ksort($this->mediaFiles, SORT_STRING);
?>
<ul class="directory-tree treeselect">
<?php foreach ($this->mediaFiles as $key => $value) : ?>
<?php if (is_array($value)) : ?>
<?php
$keyArray = explode('/', rtrim($key, '\\'));
$fileArray = explode('/', $this->fileName);
$count = 0;
$keyArrayCount = count($keyArray);
if (count($fileArray) >= $keyArrayCount) {
for ($i = 0; $i < $keyArrayCount; $i++) {
if ($keyArray[$i] === $fileArray[$i]) {
$count++;
}
}
if ($count === $keyArrayCount) {
$class = 'folder show';
} else {
$class = 'folder';
}
} else {
$class = 'folder';
}
?>
<li class="<?php echo $class; ?>">
<a class="folder-url" href="">
<span class="icon-folder icon-fw" aria-hidden="true"></span>&nbsp;<?php $explodeArray = explode('/', $key);
echo $this->escape(end($explodeArray)); ?>
</a>
<?php echo $this->mediaTree($value); ?>
</li>
<?php endif; ?>
<?php if (is_object($value)) : ?>
<li>
<a class="file" href="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $this->id . '&file=' . $value->id . '&isMedia=1'); ?>">
<span class="icon-file-alt" aria-hidden="true"></span>&nbsp;<?php echo $this->escape($value->name); ?>
</a>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>

View File

@ -0,0 +1,102 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2018 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\Router\Route;
HTMLHelper::_('bootstrap.dropdown', '.dropdown-toggle');
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('table.columns');
$input = Factory::getApplication()->getInput();
?>
<?php if (count($this->updatedList) === 0) : ?>
<div class="alert alert-success">
<span class="icon-check-circle" aria-hidden="true"></span><span class="visually-hidden"><?php echo Text::_('NOTICE'); ?></span>
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_UPTODATE'); ?>
</div>
<?php else : ?>
<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::_('COM_TEMPLATES_OVERRIDE_NOT_UPTODATE'); ?>
</div>
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" method="post" name="updateForm" id="updateForm">
<div class="row mt-2">
<div class="col-md-12">
<table class="table">
<caption class="visually-hidden">
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_UPDATED_FILES_CAPTION'); ?>
</caption>
<thead>
<tr>
<td class="w-5 text-center">
<?php echo HTMLHelper::_('grid.checkall'); ?>
</td>
<th scope="col" class="w-7">
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_CHECKED'); ?>
</th>
<th scope="col" class="w-30">
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_TEMPLATE_FILE'); ?>
</th>
<th scope="col">
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_CREATED_DATE'); ?>
</th>
<th scope="col">
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_MODIFIED_DATE'); ?>
</th>
<th scope="col">
<?php echo Text::_('COM_TEMPLATES_OVERRIDE_SOURCE'); ?>
</th>
</tr>
</thead>
<tbody>
<?php foreach ($this->updatedList as $i => $value) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="text-center">
<?php echo HTMLHelper::_('grid.id', $i, $value->hash_id, false, 'cid', 'cb', '', 'updateForm'); ?>
</td>
<td>
<?php echo HTMLHelper::_('jgrid.published', $value->state, $i, 'template.', 1, 'cb', null, null, 'updateForm'); ?>
</td>
<td>
<a href="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . (int) $value->extension_id . '&file=' . $value->hash_id); ?>" title="<?php echo Text::_('JACTION_EDIT'); ?>"><?php echo base64_decode($value->hash_id); ?></a>
</td>
<td>
<?php $created_date = $value->created_date; ?>
<?php echo $created_date > 0 ? HTMLHelper::_('date', $created_date, Text::_('DATE_FORMAT_FILTER_DATETIME')) : '-'; ?>
</td>
<td>
<?php if (is_null($value->modified_date)) : ?>
<span class="badge bg-warning text-dark"><?php echo Text::_('COM_TEMPLATES_OVERRIDE_CORE_REMOVED'); ?></span>
<?php else : ?>
<?php echo HTMLHelper::_('date', $value->modified_date, Text::_('DATE_FORMAT_FILTER_DATETIME')); ?>
<?php endif; ?>
</td>
<td>
<span class="badge bg-info"><?php echo $value->action; ?></span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<input type="hidden" name="task" value="">
<input type="hidden" name="boxchecked" value="0">
<?php echo HTMLHelper::_('form.token'); ?>
</div>
</div>
</form>
<?php endif; ?>

View File

@ -0,0 +1,32 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2015 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\Router\Route;
$input = Factory::getApplication()->getInput();
?>
<form action="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" method="post" name="adminForm" id="adminForm">
<?php echo HTMLHelper::_('uitab.startTabSet', 'myTab', ['active' => 'description', 'recall' => true, 'breakpoint' => 768]); ?>
<?php echo HTMLHelper::_('uitab.addTab', 'myTab', 'description', Text::_('COM_TEMPLATES_TAB_DESCRIPTION')); ?>
<div class="card">
<div class="card-body">
<?php echo $this->loadTemplate('description'); ?>
</div>
</div>
<?php echo HTMLHelper::_('uitab.endTab'); ?>
<?php echo HTMLHelper::_('uitab.endTabSet'); ?>
<input type="hidden" name="task" value="">
<?php echo HTMLHelper::_('form.token'); ?>
</form>

View File

@ -0,0 +1,144 @@
<?php
/**
* @package Joomla.Administrator
* @subpackage com_templates
*
* @copyright (C) 2008 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;
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('table.columns')
->useScript('multiselect');
$user = $this->getCurrentUser();
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
?>
<form action="<?php echo Route::_('index.php?option=com_templates&view=templates'); ?>" method="post" name="adminForm" id="adminForm">
<div class="row">
<div class="col-md-12">
<div id="j-main-container" class="j-main-container">
<?php echo LayoutHelper::render('joomla.searchtools.default', ['view' => $this, 'options' => ['selectorFieldName' => 'client_id']]); ?>
<?php if ($this->total > 0) : ?>
<table class="table" id="templateList">
<caption class="visually-hidden">
<?php echo Text::_('COM_TEMPLATES_TEMPLATES_TABLE_CAPTION'); ?>,
<span id="orderedBy"><?php echo Text::_('JGLOBAL_SORTED_BY'); ?> </span>,
<span id="filteredBy"><?php echo Text::_('JGLOBAL_FILTERED_BY'); ?></span>
</caption>
<thead>
<tr>
<th scope="col" class="w-20 col1template d-none d-md-table-cell">
<?php echo Text::_('COM_TEMPLATES_HEADING_IMAGE'); ?>
</th>
<th scope="col" class="w-30">
<?php echo HTMLHelper::_('searchtools.sort', 'COM_TEMPLATES_HEADING_TEMPLATE', 'a.element', $listDirn, $listOrder); ?>
</th>
<th scope="col" class="w-10 d-none d-md-table-cell text-center">
<?php echo Text::_('JVERSION'); ?>
</th>
<th scope="col" class="w-10 d-none d-md-table-cell text-center">
<?php echo Text::_('JDATE'); ?>
</th>
<th scope="col" class="w-25 d-none d-md-table-cell text-center">
<?php echo Text::_('JAUTHOR'); ?>
</th>
<?php if ($this->pluginState) : ?>
<th scope="col" class="w-10 d-none d-md-table-cell text-center">
<?php echo Text::_('COM_TEMPLATES_OVERRIDES'); ?>
</th>
<?php endif; ?>
</tr>
</thead>
<tbody>
<?php foreach ($this->items as $i => $item) : ?>
<tr class="row<?php echo $i % 2; ?>">
<td class="text-center d-none d-md-table-cell">
<?php echo HTMLHelper::_('templates.thumb', $item); ?>
<?php echo HTMLHelper::_('templates.thumbModal', $item); ?>
</td>
<th scope="row" class="template-name">
<a href="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . (int) $item->extension_id . '&file=' . $this->file); ?>">
<?php echo Text::sprintf('COM_TEMPLATES_TEMPLATE_DETAILS', ucfirst($item->name)); ?></a>
<div>
<?php if ($this->preview) : ?>
<?php $client = (int) $item->client_id === 1 ? 'administrator' : 'site'; ?>
<a href="<?php echo Route::link($client, 'index.php?tp=1&template=' . $item->element); ?>" target="_blank" aria-labelledby="preview-<?php echo $item->extension_id; ?>">
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_PREVIEW'); ?>
</a>
<div role="tooltip" id="preview-<?php echo $item->extension_id; ?>"><?php echo Text::sprintf('COM_TEMPLATES_TEMPLATE_NEW_PREVIEW', $item->name); ?></div>
<?php else : ?>
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_NO_PREVIEW'); ?>
<?php endif; ?>
</div>
<?php if (isset($item->xmldata->inheritable) && $item->xmldata->inheritable) : ?>
<div class="badge bg-primary">
<span class="fas fa-link text-light" aria-hidden="true"></span>
<?php echo Text::_('COM_TEMPLATES_TEMPLATE_IS_PARENT'); ?>
</div>
<?php endif; ?>
<?php if (isset($item->xmldata->parent) && (string) $item->xmldata->parent !== '') : ?>
<div class="badge bg-info text-light">
<span class="fas fa-clone text-light" aria-hidden="true"></span>
<?php echo Text::sprintf('COM_TEMPLATES_TEMPLATE_IS_CHILD_OF', (string) $item->xmldata->parent); ?>
</div>
<?php endif; ?>
</th>
<td class="small d-none d-md-table-cell text-center">
<?php echo $this->escape($item->xmldata->get('version')); ?>
</td>
<td class="small d-none d-md-table-cell text-center">
<?php echo $this->escape($item->xmldata->get('creationDate')); ?>
</td>
<td class="d-none d-md-table-cell text-center">
<?php if ($author = $item->xmldata->get('author')) : ?>
<div><?php echo $this->escape($author); ?></div>
<?php else : ?>
&mdash;
<?php endif; ?>
<?php if ($email = $item->xmldata->get('authorEmail')) : ?>
<div><?php echo $this->escape($email); ?></div>
<?php endif; ?>
<?php if ($url = $item->xmldata->get('authorUrl')) : ?>
<div><a href="<?php echo $this->escape($url); ?>"><?php echo $this->escape($url); ?></a></div>
<?php endif; ?>
</td>
<?php if ($this->pluginState) : ?>
<td class="d-none d-md-table-cell text-center">
<?php if (!empty($item->updated)) : ?>
<a href="<?php echo Route::_('index.php?option=com_templates&view=template&id=' . (int) $item->extension_id . '#files'); ?>">
<span class="badge bg-warning text-dark"><?php echo Text::plural('COM_TEMPLATES_N_CONFLICT', $item->updated); ?></span>
</a>
<?php else : ?>
<span class="badge bg-success"><?php echo Text::_('COM_TEMPLATES_UPTODATE'); ?></span>
<?php endif; ?>
</td>
<?php endif; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php // load the pagination. ?>
<?php 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>
</div>
</div>
</form>

View File

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