<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2021 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Outline\OutlineCollection;
use Gantry\Debugger;
use Gantry\Joomla\StyleHelper;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Factory;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Class Outlines
* @package Gantry\Framework
*/
class Outlines extends OutlineCollection
{
/** @var int */
protected $createId;
/**
* @param int|string $id
* @return int|string
*/
public function preset($id)
{
if (is_numeric($id)) {
$style = StyleHelper::getStyle($id);
$params = json_decode($style->params, true);
$id = isset($params['preset']) ? $params['preset'] : 'default';
}
return $id;
}
/**
* @param object|null $template
* @return mixed
*/
public function current($template = null)
{
/** @var CMSApplication $application */
$application = Factory::getApplication();
if (!is_object($template)) {
// Get the template style.
$template = $application->getTemplate(true);
}
$preset = $template->params->get('preset', 'default');
$outline = $template->params->get('configuration', !empty($template->id) ? $template->id : null);
if (\GANTRY_DEBUGGER) {
Debugger::addMessage('Template Style:' . $template);
}
if (JDEBUG && !$outline) {
static $shown = false;
if (!$shown) {
$shown = true;
$application->enqueueMessage('[DEBUG] JApplicationSite::getTemplate() was overridden with no specified Gantry 5 outline.', 'notice');
}
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
return ($outline && is_dir($locator("{$this->path}/{$outline}"))) ? $outline : $preset;
}
/**
* @param string $path
* @return $this
*/
public function load($path = 'gantry-config://')
{
$this->path = $path;
$gantry = $this->container;
$theme = isset($gantry['theme.name']) ? $gantry['theme.name'] : null;
$styles = ThemeList::getStyles($theme);
$installer = new ThemeInstaller($this->container['theme.name']);
$title = $installer->getStyleName('%s - ');
$outlines = [];
foreach ($styles as $style) {
$preset = isset($style->params['preset']) ? $style->params['preset'] : null;
$outline = isset($style->params['configuration']) ? $style->params['configuration'] : $preset;
if ($outline && $outline != $style->id) {
// New style generated by Joomla.
StyleHelper::copy($style, $outline, $style->id);
}
$outlines[$style->id] = preg_replace('|^' . preg_quote($title, '|') . '|', '', $style->style);
}
asort($outlines);
$this->items = $this->addDefaults($outlines);
return $this;
}
/**
* @param string|null $id
* @param string $title
* @param string|array $preset
* @return string
* @throws \RuntimeException
*/
public function create($id, $title = null, $preset = null)
{
if ($this->createId) {
// Workaround Joomla wanting to use different logic for style duplication.
$new = parent::create($this->createId, $title, $preset);
$this->createId = null;
return $new;
}
$title = $title ? "%s - {$title}" : '%s - Untitled';
$installer = new ThemeInstaller($this->container['theme.name']);
$title = $installer->getStyleName($title);
$style = $installer->addStyle($title);
$error = $style->getError();
if ($error) {
throw new \RuntimeException($error, 400);
}
$presetId = (string) (isset($preset['preset']['name']) ? $preset['preset']['name'] : ($preset ?: 'default'));
StyleHelper::update($style->id, $presetId);
// Create configuration folder.
$newId = parent::create($style->id, $title, $preset);
if ($newId != $style->id) {
throw new \RuntimeException(sprintf("Creating outline: folder '%s' already exists!", $style->id));
}
return $style->id;
}
/**
* @param string $id
* @param null $title
* @param bool $inherit
* @return string
* @throws \RuntimeException
*/
public function duplicate($id, $title = null, $inherit = false)
{
if (!$this->canDuplicate($id)) {
throw new \RuntimeException("Outline '$id' cannot be duplicated", 400);
}
// Handle special case of duplicating system outlines.
if ((string)(int) $id !== (string) $id) {
return parent::duplicate($id, $title, $inherit);
}
// Use Joomla logic to duplicate the style.
$model = StyleHelper::loadModel();
$pks = [$id];
try {
$model->duplicate($pks);
} catch (\Exception $e) {
throw new \RuntimeException($e->getMessage(), 400, $e);
}
// Seek the newly generated style ID since Joomla doesn't return one on duplication.
$theme = $this->container['theme.name'];
$styles = ThemeList::getStyles($theme, true);
$style = end($styles);
if ($title) {
// Change the title.
$installer = new ThemeInstaller($theme);
$title = $installer->getStyleName("%s - {$title}");
$this->rename($style->id, $title);
} else {
$title = $style->style;
}
$this->createId = $style->id;
return parent::duplicate($id, $title, $inherit);
}
/**
* @param string $id
* @param string $title
* @return string
* @throws \RuntimeException
*/
public function rename($id, $title)
{
$model = StyleHelper::loadModel();
$item = $model->getTable();
$item->load($id);
if (!$item->id) {
throw new \RuntimeException('Outline not found', 404);
}
$theme = $this->container['theme.name'];
$installer = new ThemeInstaller($theme);
$title = $title ? "%s - {$title}" : '%s - Untitled';
$title = $installer->getStyleName($title);
$item->title = $title;
if (!$item->check()) {
throw new \RuntimeException($item->getError(), 400);
}
if (!$item->store()) {
throw new \RuntimeException($item->getError(), 500);
}
if (isset($this->items[$id])) {
$this->items[$id] = $title;
}
return $id;
}
/**
* @param string $id
* @param bool $deleteModel
* @throws \Exception
*/
public function delete($id, $deleteModel = true)
{
if (!$this->canDelete($id)) {
throw new \RuntimeException("Outline '$id' cannot be deleted", 400);
}
$model = StyleHelper::loadModel();
$item = $model->getTable();
$item->load($id);
try {
foreach ($this->getInheritingOutlines($id) as $outline => $title) {
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
}
foreach ($this->getInheritingOutlinesWithAtom($id) as $outline => $title) {
Atoms::instance($outline)->updateInheritance($id)->save();
}
if ($deleteModel && !$model->delete($id)) {
$error = $model->getError();
// Well, Joomla can always send enqueue message instead!
if (!$error) {
$messages = Factory::getApplication()->getMessageQueue();
$message = reset($messages);
$error = $message ? $message['message'] : 'Unknown error';
}
throw new \RuntimeException($error);
}
} catch (\Exception $e) {
throw new \RuntimeException('Deleting outline failed: ' . $e->getMessage(), 400, $e);
}
// Remove configuration directory.
$gantry = $this->container;
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$path = $locator->findResource("{$this->path}/{$item->id}", true, true);
if ($path) {
if (file_exists($path)) {
Folder::delete($path);
}
}
unset($this->items[$item->id]);
}
/**
* @param string $id
* @return boolean
*/
public function canDelete($id)
{
$style = StyleHelper::getStyle($id);
return !(!$style->id || $style->home);
}
/**
* @param string $id
* @return boolean
*/
public function isDefault($id)
{
$style = StyleHelper::getStyle($id);
return (bool) $style->home;
}
}