Your IP : 216.73.216.61


Current Path : /home/w/u/e/wuectly/www/03cbe/
Upload File :
Current File : /home/w/u/e/wuectly/www/03cbe/View.tar

Engine/BladeEngine.php000060400000001052152455612700010620 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\View\View;

/**
 * View engine for compiling PHP template files.
 */
class BladeEngine extends CompilingEngine implements EngineInterface
{
	public function __construct(View $view)
	{
		parent::__construct($view);

		// Assign the Blade compiler to this engine
		$this->compiler = $view->getContainer()->blade;
	}
}
Engine/CompilingEngine.php000060400000015262152455612700011542 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\Utils\Buffer;
use FOF40\View\Compiler\CompilerInterface;
use FOF40\View\Exception\PossiblySuhosin;
use Joomla\CMS\Filesystem\File;

/**
 * View engine for compiling PHP template files.
 */
abstract class CompilingEngine extends AbstractEngine implements EngineInterface
{
	/** @var  CompilerInterface  The compiler used by this engine */
	protected $compiler;

	/**
	 * Get the 3ναlυa+3d contents of the view template. (I use leetspeak here because of bad quality hosts with broken
	 * scanners)
	 *
	 * @param   string  $path         The path to the view template
	 * @param   array   $forceParams  Any additional information to pass to the view template engine
	 *
	 * @return  array  Content evaluation information
	 */
	public function get($path, array $forceParams = [])
	{
		// If it's cached return the path to the cached file's path
		if ($this->isCached($path))
		{
			return [
				'type'    => 'path',
				'content' => $this->getCachePath($path),
			];
		}

		/**
		 * Compile and cache the file. We also add the file path in a comment at the top of the file so phpStorm can
		 * debug it.
		 *
		 * @see https://blog.jetbrains.com/phpstorm/2019/02/phpstorm-2019-1-eap-191-5849-26/
		 * @see https://laravel-news.com/laravel-5-8-blade-template-file-path
		 */
		$content   = "<?php /* $path */ ?>\n";
		$content   .= $this->compile($path, $forceParams);
		$cachePath = $this->putToCache($path, $content);
		$isPHPFile = substr($path, -4) == '.php';

		// If we could cache it, return the cached file's path
		if ($cachePath !== false)
		{
			// Bust the opcode cache for .php files
			if ($isPHPFile)
			{
				$this->bustOpCache($path);
			}

			return [
				'type'    => 'path',
				'content' => $cachePath,
			];
		}

		// We could not write to the cache. Hm, can I use a stream wrapper?
		$canUseStreams = Buffer::canRegisterWrapper();

		if ($canUseStreams)
		{
			$id         = $this->getIdentifier($path);
			$streamPath = 'fof://' . $this->view->getContainer()->componentName . '/compiled_templates/' . $id . '.php';
			file_put_contents($streamPath, $content);

			// Bust the opcode cache for .php files
			if ($isPHPFile)
			{
				$this->bustOpCache($path);
			}

			return [
				'type'    => 'path',
				'content' => $streamPath,
			];
		}

		// I couldn't use a stream wrapper. I have to give up.
		throw new PossiblySuhosin;
	}

	/**
	 * Returns the path where I can find a precompiled version of the uncompiled view template which lives in $path
	 *
	 * @param   string  $path  The path to the uncompiled view template
	 *
	 * @return  bool|string  False if the view template is outside the component's front- or backend.
	 *
	 * @since   3.3.1
	 */
	public function getPrecompiledPath($path)
	{
		// Normalize the path to the file
		$path = realpath($path);

		if ($path === false)
		{
			// The file doesn't exist
			return false;
		}

		// Is this path under the component's front- or backend?
		$frontendPath = realpath($this->view->getContainer()->frontEndPath);
		$backendPath  = realpath($this->view->getContainer()->backEndPath);

		$backPos  = strpos($path, $backendPath);
		$frontPos = strpos($path, $frontendPath);

		if (($backPos !== 0) && ($frontPos !== 0))
		{
			// This is not a view template shipped with the component, i.e. it can't be precompiled
			return false;
		}

		// Eliminate the component path from $path to get the relative path to the file
		$componentPath = $frontendPath;

		if ($backPos === 0)
		{
			$componentPath = $backendPath;
		}

		$relativePath = ltrim(substr($path, strlen($componentPath)), '\\/');

		// Break down the relative path to its parts
		$relativePath = str_replace('\\', '/', $relativePath);
		$pathParts    = explode('/', $relativePath);

		// Remove the prefix
		$prefix = array_shift($pathParts);

		// If it's a legacy view, View, Views, or views prefix remove the 'tmpl' part
		if (($prefix !== 'ViewTemplates') && ($prefix !== 'tmpl'))
		{
			unset($pathParts[1]);
		}

		// Get the last part and process the extension
		$viewFile            = array_pop($pathParts);
		$extensionWithoutDot = $this->compiler->getFileExtension();
		$pathParts[]         = substr($viewFile, 0, -strlen($extensionWithoutDot)) . 'php';

		$precompiledRelativePath = implode(DIRECTORY_SEPARATOR, $pathParts);

		return $componentPath . DIRECTORY_SEPARATOR . 'PrecompiledTemplates' . DIRECTORY_SEPARATOR . $precompiledRelativePath;
	}

	/**
	 * A method to compile the raw view template into valid PHP
	 *
	 * @param   string  $path         The path to the view template
	 * @param   array   $forceParams  Any additional information to pass to the view template compiler
	 *
	 * @return  string  The template compiled to executable PHP
	 */
	protected function compile($path, array $forceParams = [])
	{
		return $this->compiler->compile($path, $forceParams);
	}

	protected function getIdentifier($path)
	{
		if (function_exists('sha1'))
		{
			return sha1($path);
		}

		return md5($path);
	}

	protected function getCachePath($path)
	{
		$id = $this->getIdentifier($path);

		return JPATH_CACHE . '/' . $this->view->getContainer()->componentName . '/compiled_templates/' . $id . '.php';
	}

	protected function isCached($path)
	{
		if (!$this->compiler->isCacheable())
		{
			return false;
		}

		$cachePath = $this->getCachePath($path);

		if (!file_exists($cachePath))
		{
			return false;
		}

		$cacheTime = filemtime($cachePath);
		$fileTime  = filemtime($path);

		return $fileTime <= $cacheTime;
	}

	protected function getCached($path)
	{
		$cachePath = $this->getCachePath($path);

		return file_get_contents($cachePath);
	}

	protected function putToCache($path, $content)
	{
		$cachePath = $this->getCachePath($path);

		if (@file_put_contents($cachePath, $content))
		{
			return $cachePath;
		}

		if (File::write($cachePath, $content))
		{
			return $cachePath;
		}

		return false;
	}

	/**
	 * Bust the opcode cache for a given .php file
	 *
	 * This method can address opcode caching with:
	 * - Zend OPcache
	 * - Alternative PHP Cache (now defunct)
	 * - Windows Cache Extension for PHP (versions lower than 2.0.0)
	 * - XCache (now defunct)
	 *
	 * @param   string  $path  The file to bus the cache for
	 *
	 * @return  void
	 */
	private function bustOpCache($path)
	{
		if (function_exists('opcache_invalidate'))
		{
			opcache_invalidate($path);
		}

		if (function_exists('apc_compile_file'))
		{
			apc_compile_file($path);
		}

		if (function_exists('wincache_refresh_if_changed'))
		{
			wincache_refresh_if_changed([$path]);
		}

		if (function_exists('xcache_asm'))
		{
			xcache_asm($path);
		}
	}
}Engine/PhpEngine.php000060400000001650152455612700010344 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\View\Engine;

defined('_JEXEC') || die;

/**
 * View engine for plain PHP template files (no translation).
 */
class PhpEngine extends AbstractEngine implements EngineInterface
{
	/**
	 * Get the 3ναluα+3d contents of the view template. (I use leetspeak here because of bad quality hosts with broken scanners)
	 *
	 * @param   string  $path         The path to the view template
	 * @param   array   $forceParams  Any additional information to pass to the view template engine
	 *
	 * @return  array  Content 3ναlυα+ιοη information (I use leetspeak here because of asshole hosts with broken scanners)
	 */
	public function get($path, array $forceParams = array())
	{
		return array(
			'type'    => 'path',
			'content' => $path
		);
	}
}
Engine/EngineInterface.php000060400000001572152455612700011520 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\View\View;

interface EngineInterface
{
	/**
	 * Public constructor
	 *
	 * @param   View  $view  The view we belong to
	 */
	public function __construct(View $view);

	/**
	 * Get the include path for a parsed view template
	 *
	 * @param   string  $path         The path to the view template
	 * @param   array   $forceParams  Any additional information to pass to the view template engine
	 *
	 * @return  array  Content 3ναlυα+ιοη information ['type' => 'raw|path', 'content' => 'path or raw content'] (I use leetspeak here because of bad quality hosts with broken scanners)
	 */
	public function get($path, array $forceParams = array());
}
Engine/AbstractEngine.php000060400000002006152455612700011354 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\View\View;

abstract class AbstractEngine implements EngineInterface
{
	/** @var   View  The view we belong to */
	protected $view;

	/**
	 * Public constructor
	 *
	 * @param   View  $view  The view we belong to
	 */
	public function __construct(View $view)
	{
		$this->view = $view;
	}

	/**
	 * Get the include path for a parsed view template
	 *
	 * @param   string  $path         The path to the view template
	 * @param   array   $forceParams  Any additional information to pass to the view template engine
	 *
	 * @return  array  Content 3ναlυα+ιοη information (I use leetspeak here because of bad quality hosts with broken
	 *                 scanners)
	 */
	public function get($path, array $forceParams = [])
	{
		return [
			'type'    => 'raw',
			'content' => '',
		];
	}
}
Exception/EmptyStack.php000060400000001200152455612700011273 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

use Exception;
use Joomla\CMS\Language\Text;
use RuntimeException;

/**
 * Exception thrown when we are trying to operate on an empty section stack
 */
class EmptyStack extends RuntimeException
{
	public function __construct(string $message = "", int $code = 500, Exception $previous = null)
	{
		$message = Text::_('LIB_FOF40_VIEW_EMPTYSECTIONSTACK');

		parent::__construct($message, $code, $previous);
	}
}
Exception/ModelNotFound.php000060400000001220152455612700011726 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

use Exception;
use Joomla\CMS\Language\Text;
use RuntimeException;

/**
 * Exception thrown when we can't get a Controller's name
 */
class ModelNotFound extends RuntimeException
{
	public function __construct(string $path, string $viewName, int $code = 500, Exception $previous = null)
	{
		$message = Text::sprintf('LIB_FOF40_VIEW_MODELNOTINVIEW', $path, $viewName);

		parent::__construct($message, $code, $previous);
	}
}
Exception/UnrecognisedExtension.php000060400000001256152455612700013544 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\View\Exception;

defined('_JEXEC') || die;

use Exception;
use InvalidArgumentException;
use Joomla\CMS\Language\Text;

/**
 * Exception thrown when we can't figure out which engine to use for a view template
 */
class UnrecognisedExtension extends InvalidArgumentException
{
	public function __construct(string $path, int $code = 500, Exception $previous = null)
	{
		$message = Text::sprintf('LIB_FOF40_VIEW_UNRECOGNISEDEXTENSION', $path);

		parent::__construct($message, $code, $previous);
	}
}
Exception/CannotGetName.php000060400000001214152455612700011677 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

use Exception;
use Joomla\CMS\Language\Text;
use RuntimeException;

/**
 * Exception thrown when we can't get a Controller's name
 */
class CannotGetName extends RuntimeException
{
	public function __construct(string $message = "", int $code = 500, Exception $previous = null)
	{
		if (empty($message))
		{
			$message = Text::_('LIB_FOF40_VIEW_ERR_GET_NAME');
		}

		parent::__construct($message, $code, $previous);
	}
}
Exception/PossiblySuhosin.php000060400000001307152455612700012374 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

use Exception;
use Joomla\CMS\Language\Text;
use RuntimeException;

/**
 * Exception thrown when the access to the requested resource is forbidden under the current execution context
 */
class PossiblySuhosin extends RuntimeException
{
	public function __construct(string $message = "", int $code = 403, Exception $previous = null)
	{
		if (empty($message))
		{
			$message = Text::_('LIB_FOF40_VIEW_POSSIBLYSUHOSIN');
		}

		parent::__construct($message, $code, $previous);
	}

}
Exception/AccessForbidden.php000060400000001320152455612700012230 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

use Exception;
use Joomla\CMS\Language\Text;
use RuntimeException;

/**
 * Exception thrown when the access to the requested resource is forbidden under the current execution context
 */
class AccessForbidden extends RuntimeException
{
	public function __construct(string $message = "", int $code = 403, Exception $previous = null)
	{
		if (empty($message))
		{
			$message = Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN');
		}

		parent::__construct($message, $code, $previous);
	}

}
Compiler/CompilerInterface.php000060400000001653152455612700012432 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Compiler;

defined('_JEXEC') || die;

interface CompilerInterface
{
	/**
	 * Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return
	 * false.
	 *
	 * @return  bool
	 */
	public function isCacheable(): bool;

	/**
	 * Compile a view template into PHP and HTML
	 *
	 * @param string $path        The absolute filesystem path of the view template
	 * @param array  $forceParams Any parameters to force (only for engines returning raw HTML)
	 *
	 * @return mixed
	 */
	public function compile(string $path, array $forceParams = []);

	/**
	 * Returns the file extension supported by this compiler
	 *
	 * @return  string
	 *
	 * @since   3.3.1
	 */
	public function getFileExtension(): string;
}
Compiler/Blade.php000060400000064375152455612700010060 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\Compiler;

defined('_JEXEC') || die;

use FOF40\Container\Container;

class Blade implements CompilerInterface
{
	/**
	 * Are the results of this engine cacheable?
	 *
	 * @var bool
	 */
	protected $isCacheable = true;

	/**
	 * The extension of the template files supported by this compiler
	 *
	 * @var    string
	 * @since  3.3.1
	 */
	protected $fileExtension = 'blade.php';

	/**
	 * All of the registered compiler extensions.
	 *
	 * @var array
	 */
	protected $extensions = [];

	/**
	 * The file currently being compiled.
	 *
	 * @var string
	 */
	protected $path;

	/**
	 * All of the available compiler functions. Each one is called against every HTML block in the template.
	 *
	 * @var array
	 */
	protected $compilers = [
		'Extensions',
		'Statements',
		'Comments',
		'Echos',
	];

	/**
	 * Array of opening and closing tags for escaped echos.
	 *
	 * @var array
	 */
	protected $contentTags = ['{{', '}}'];

	/**
	 * Array of opening and closing tags for escaped echos.
	 *
	 * @var array
	 */
	protected $escapedTags = ['{{{', '}}}'];

	/**
	 * Array of footer lines to be added to template.
	 *
	 * @var array
	 */
	protected $footer = [];

	/**
	 * Counter to keep track of nested forelse statements.
	 *
	 * @var int
	 */
	protected $forelseCounter = 0;

	/**
	 * The FOF container we are attached to
	 *
	 * @var Container
	 */
	protected $container;

	/**
	 * Should I use the PHP Tokenizer extension to compile Blade templates? Default is true and is preferable. We expect
	 * this to be false only on bad quality hosts. It can be overridden with Reflection for testing purposes.
	 *
	 * @var bool
	 */
	protected $usingTokenizer = false;

	public function __construct(Container $container)
	{
		$this->container      = $container;
		$this->usingTokenizer = false;

		if (!function_exists('token_get_all'))
		{
			return;
		}

		if (!defined('T_INLINE_HTML'))
		{
			return;
		}

		$this->usingTokenizer = true;
	}

	/**
	 * Report if the PHP Tokenizer extension is being used
	 *
	 * @return  bool
	 */
	public function isUsingTokenizer(): bool
	{
		return $this->usingTokenizer;
	}

	/**
	 * Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return
	 * false.
	 *
	 * @return  bool
	 */
	public function isCacheable(): bool
	{
		if (defined('JDEBUG') && JDEBUG)
		{
			return false;
		}

		return $this->isCacheable;
	}

	/**
	 * Compile a view template into PHP and HTML
	 *
	 * @param   string  $path         The absolute filesystem path of the view template
	 * @param   array   $forceParams  Any parameters to force (only for engines returning raw HTML)
	 *
	 * @return string The compiled result
	 */
	public function compile(?string $path, array $forceParams = []): string
	{
		if (empty($path))
		{
			return '';
		}

		$this->footer = [];

		$fileData = @file_get_contents($path);

		$this->setPath($path);

		return $this->compileString($fileData);
	}


	/**
	 * Get the path currently being compiled.
	 *
	 * @return string
	 */
	public function getPath(): string
	{
		return $this->path;
	}

	/**
	 * Set the path currently being compiled.
	 *
	 * @param   string  $path
	 *
	 * @return void
	 */
	public function setPath(string $path): void
	{
		$this->path = $path;
	}

	/**
	 * Compile the given Blade template contents.
	 *
	 * @param   string  $value
	 *
	 * @return string
	 */
	public function compileString(string $value): string
	{
		$result = '';

		if ($this->usingTokenizer)
		{
			// Here we will loop through all of the tokens returned by the Zend lexer and
			// parse each one into the corresponding valid PHP. We will then have this
			// template as the correctly rendered PHP that can be rendered natively.
			foreach (token_get_all($value) as $token)
			{
				$result .= is_array($token) ? $this->parseToken($token) : $token;
			}
		}
		else
		{
			foreach ($this->compilers as $type)
			{
				$value = $this->{"compile{$type}"}($value);
			}

			$result .= $value;
		}

		// If there are any footer lines that need to get added to a template we will
		// add them here at the end of the template. This gets used mainly for the
		// template inheritance via the extends keyword that should be appended.
		if (count($this->footer) > 0)
		{
			$result = ltrim($result, PHP_EOL)
				. PHP_EOL . implode(PHP_EOL, array_reverse($this->footer));
		}

		return $result;
	}

	/**
	 * Compile the default values for the echo statement.
	 *
	 * @param   string  $value
	 *
	 * @return string
	 */
	public function compileEchoDefaults(string $value): string
	{
		return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/s', 'isset($1) ? $1 : $2', $value);
	}

	/**
	 * Register a custom Blade compiler. Using a tag or not changes the behavior of this method when you try to redefine
	 * an existing custom Blade compiler.
	 *
	 * If you use a tag which already exists the old compiler is replaced by the new one you are defining.
	 *
	 * If you do not use a tag, the new compiler you are defining will always be added to the bottom of the list. That
	 * is to say, if another compiler would be matching the same function name (e.g. `@foobar`) it would get compiled by
	 * the first compiler, the one already set, not the one you are defining now. You are suggested to always use a tag
	 * for this reason.
	 *
	 * Finally, note that custom Blade compilers are compiled last. This means that you cannot override a core Blade
	 * compiler with a custom one. If you need to do that you need to create a new Compiler class -- probably extending
	 * this one -- and override the protected compiler methods. Remember to also create a custom Container and override
	 * its 'blade' key with a callable which returns an object of your custom class.
	 *
	 * @param   callable  $compiler
	 * @param   string    $tag  Optional. Give the callable a tag you can look for with hasExtensionByName
	 *
	 * @return void
	 */
	public function extend(callable $compiler, ?string $tag = null)
	{
		if (!is_null($tag))
		{
			$this->extensions[$tag] = $compiler;

			return;
		}

		$this->extensions[] = $compiler;
	}

	/**
	 * Look if a custom Blade compiler exists given its optional tag name.
	 *
	 * @param   string  $tag
	 *
	 * @return  bool
	 */
	public function hasExtension(string $tag): bool
	{
		return array_key_exists($tag, $this->extensions);
	}

	/**
	 * Remove a custom BLade compiler given its optional tag name
	 *
	 * @param   string  $tag
	 */
	public function removeExtension(string $tag): void
	{
		if (!$this->hasExtension($tag))
		{
			return;
		}

		unset ($this->extensions[$tag]);
	}

	/**
	 * Get the regular expression for a generic Blade function.
	 *
	 * @param   string  $function
	 *
	 * @return string
	 */
	public function createMatcher(string $function): string
	{
		return '/(?<!\w)(\s*)@' . $function . '(\s*\(.*\))/';
	}

	/**
	 * Get the regular expression for a generic Blade function.
	 *
	 * @param   string  $function
	 *
	 * @return string
	 */
	public function createOpenMatcher(string $function): string
	{
		return '/(?<!\w)(\s*)@' . $function . '(\s*\(.*)\)/';
	}

	/**
	 * Create a plain Blade matcher.
	 *
	 * @param   string  $function
	 *
	 * @return string
	 */
	public function createPlainMatcher(string $function): string
	{
		return '/(?<!\w)(\s*)@' . $function . '(\s*)/';
	}

	/**
	 * Sets the escaped content tags used for the compiler.
	 *
	 * @param   string  $openTag
	 * @param   string  $closeTag
	 *
	 * @return void
	 */
	public function setEscapedContentTags(string $openTag, string $closeTag): void
	{
		$this->setContentTags($openTag, $closeTag, true);
	}

	/**
	 * Gets the content tags used for the compiler.
	 *
	 * @return array
	 */
	public function getContentTags(): array
	{
		return $this->getTags();
	}

	/**
	 * Sets the content tags used for the compiler.
	 *
	 * @param   string  $openTag
	 * @param   string  $closeTag
	 * @param   bool    $escaped
	 *
	 * @return void
	 */
	public function setContentTags(string $openTag, string $closeTag, bool $escaped = false): void
	{
		$property = $escaped ? 'escapedTags' : 'contentTags';

		$this->{$property} = [preg_quote($openTag), preg_quote($closeTag)];
	}

	/**
	 * Gets the escaped content tags used for the compiler.
	 *
	 * @return array
	 */
	public function getEscapedContentTags(): array
	{
		return $this->getTags(true);
	}

	/**
	 * Returns the file extension supported by this compiler
	 *
	 * @return  string
	 *
	 * @since   3.3.1
	 */
	public function getFileExtension(): string
	{
		return $this->fileExtension;
	}

	/**
	 * Parse the tokens from the template.
	 *
	 * @param   array  $token  The token definition as an array of shape [tokenID, content].
	 *
	 * @return string
	 */
	protected function parseToken(array $token): string
	{
		[$id, $content] = $token;

		if ($id == T_INLINE_HTML)
		{
			foreach ($this->compilers as $type)
			{
				$content = $this->{"compile{$type}"}($content);
			}
		}

		return $content;
	}

	/**
	 * Execute the user defined extensions.
	 *
	 * @param   string  $value
	 *
	 * @return string
	 */
	protected function compileExtensions(string $value): string
	{
		foreach ($this->extensions as $compiler)
		{
			$value = call_user_func($compiler, $value, $this);
		}

		return $value;
	}

	/**
	 * Compile Blade comments into valid PHP.
	 *
	 * @param   string  $value
	 *
	 * @return string
	 */
	protected function compileComments(string $value): string
	{
		$pattern = sprintf('/%s--((.|\s)*?)--%s/', $this->contentTags[0], $this->contentTags[1]);

		return preg_replace($pattern, '<?php /*$1*/ ?>', $value);
	}

	/**
	 * Compile Blade echos into valid PHP.
	 *
	 * @param   string  $value
	 *
	 * @return string
	 */
	protected function compileEchos(string $value): string
	{
		$difference = strlen($this->contentTags[0]) - strlen($this->escapedTags[0]);

		if ($difference > 0)
		{
			return $this->compileEscapedEchos($this->compileRegularEchos($value));
		}

		return $this->compileRegularEchos($this->compileEscapedEchos($value));
	}

	/**
	 * Compile Blade Statements that start with "@"
	 *
	 * @param   string  $value
	 *
	 * @return mixed
	 */
	protected function compileStatements(string $value): string
	{
		return preg_replace_callback('/\B@(\w+)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', [
			$this, 'compileStatementsCallback',
		], $value);
	}

	/**
	 * Callback for compileStatements, since $this is not allowed in Closures under PHP 5.3.
	 *
	 * @param   $match
	 *
	 * @return  string
	 */
	protected function compileStatementsCallback(array $match): string
	{
		if (method_exists($this, $method = 'compile' . ucfirst($match[1])))
		{
			$match[0] = $this->$method(array_get($match, 3));
		}

		return isset($match[3]) ? $match[0] : $match[0] . $match[2];
	}

	/**
	 * Compile the "regular" echo statements.
	 *
	 * @param   string  $value
	 *
	 * @return  string
	 */
	protected function compileRegularEchos(string $value): string
	{
		$pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]);

		return preg_replace_callback($pattern, [$this, 'compileRegularEchosCallback'], $value);
	}

	/**
	 * Callback for compileRegularEchos, since $this is not allowed in Closures under PHP 5.3.
	 *
	 * @param   array  $matches
	 *
	 * @return  string
	 */
	protected function compileRegularEchosCallback(array $matches): string
	{
		$whitespace = empty($matches[3]) ? '' : $matches[3] . $matches[3];

		return $matches[1] ? substr($matches[0], 1) : '<?php echo ' . $this->compileEchoDefaults($matches[2]) . '; ?>' . $whitespace;
	}

	/**
	 * Compile the escaped echo statements.
	 *
	 * @param   string  $value
	 *
	 * @return string
	 */
	protected function compileEscapedEchos(string $value): string
	{
		$pattern = sprintf('/%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]);

		return preg_replace_callback($pattern, [$this, 'compileEscapedEchosCallback'], $value);
	}

	/**
	 * Callback for compileEscapedEchos, since $this is not allowed in Closures under PHP 5.3.
	 *
	 * @param   array  $matches
	 *
	 * @return  string
	 */
	protected function compileEscapedEchosCallback(array $matches): string
	{
		$whitespace = empty($matches[2]) ? '' : $matches[2] . $matches[2];

		return '<?php echo $this->escape(' . $this->compileEchoDefaults($matches[1]) . '); ?>' . $whitespace;
	}

	/**
	 * Compile the each statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEach(?string $expression): string
	{
		return "<?php echo \$this->renderEach{$expression}; ?>";
	}

	/**
	 * Compile the yield statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileYield(?string $expression): string
	{
		return "<?php echo \$this->yieldContent{$expression}; ?>";
	}

	/**
	 * Compile the show statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileShow(?string $expression = ''): string
	{
		return "<?php echo \$this->yieldSection(); ?>";
	}

	/**
	 * Compile the section statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileSection(?string $expression): string
	{
		return "<?php \$this->startSection{$expression}; ?>";
	}

	/**
	 * Compile the append statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileAppend(?string $expression): string
	{
		return "<?php \$this->appendSection(); ?>";
	}

	/**
	 * Compile the end-section statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndsection(?string $expression): string
	{
		return "<?php \$this->stopSection(); ?>";
	}

	/**
	 * Compile the stop statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileStop(?string $expression): string
	{
		return "<?php \$this->stopSection(); ?>";
	}

	/**
	 * Compile the overwrite statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileOverwrite(?string $expression): string
	{
		return "<?php \$this->stopSection(true); ?>";
	}

	/**
	 * Compile the unless statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileUnless(?string $expression): string
	{
		return "<?php if ( ! $expression): ?>";
	}

	/**
	 * Compile the end unless statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndunless(?string $expression): string
	{
		return "<?php endif; ?>";
	}

	/**
	 * Compile the end repeatable statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileRepeatable(?string $expression): string
	{
		$expression = trim($expression, '()');
		$parts      = explode(',', $expression, 2);

		$functionName  = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0]));
		$argumentsList = $parts[1] ?? '';

		return "<?php @\$$functionName = function($argumentsList) { ?>";
	}

	/**
	 * Compile the end endRepeatable statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndRepeatable(?string $expression): string
	{
		return "<?php }; ?>";
	}

	/**
	 * Compile the end yieldRepeatable statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileYieldRepeatable(?string $expression): string
	{
		$expression = trim($expression, '()');
		$parts      = explode(',', $expression, 2);

		$functionName  = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0]));
		$argumentsList = $parts[1] ?? '';

		return "<?php \$$functionName($argumentsList); ?>";
	}

	/**
	 * Compile the lang statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileLang(?string $expression): string
	{
		return "<?php echo \\Joomla\\CMS\\Language\\Text::_$expression; ?>";
	}

	/**
	 * Compile the sprintf statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileSprintf(?string $expression): string
	{
		return "<?php echo \\Joomla\\CMS\\Language\\Text::sprintf$expression; ?>";
	}

	/**
	 * Compile the plural statements into valid PHP.
	 *
	 * e.g. @plural('COM_FOOBAR_N_ITEMS_SAVED', $countItemsSaved)
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 * @see Text::plural()
	 *
	 */
	protected function compilePlural(?string $expression): string
	{
		return "<?php echo \\Joomla\\CMS\\Language\\Text::plural$expression; ?>";
	}

	/**
	 * Compile the token statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileToken(?string $expression): string
	{
		return "<?php echo \$this->container->platform->getToken(true); ?>";
	}

	/**
	 * Compile the else statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileElse(?string $expression): string
	{
		return "<?php else: ?>";
	}

	/**
	 * Compile the for statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileFor(?string $expression): string
	{
		return "<?php for{$expression}: ?>";
	}

	/**
	 * Compile the foreach statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileForeach(?string $expression): string
	{
		return "<?php foreach{$expression}: ?>";
	}

	/**
	 * Compile the forelse statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileForelse(?string $expression): string
	{
		$empty = '$__empty_' . ++$this->forelseCounter;

		return "<?php {$empty} = true; foreach{$expression}: {$empty} = false; ?>";
	}

	/**
	 * Compile the if statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileIf(?string $expression): string
	{
		return "<?php if{$expression}: ?>";
	}

	/**
	 * Compile the else-if statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileElseif(?string $expression): string
	{
		return "<?php elseif{$expression}: ?>";
	}

	/**
	 * Compile the forelse statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEmpty(?string $expression): string
	{
		$empty = '$__empty_' . $this->forelseCounter--;

		return "<?php endforeach; if ({$empty}): ?>";
	}

	/**
	 * Compile the while statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileWhile(?string $expression): string
	{
		return "<?php while{$expression}: ?>";
	}

	/**
	 * Compile the end-while statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndwhile(?string $expression): string
	{
		return "<?php endwhile; ?>";
	}

	/**
	 * Compile the end-for statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndfor(?string $expression): string
	{
		return "<?php endfor; ?>";
	}

	/**
	 * Compile the end-for-each statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndforeach(?string $expression): string
	{
		return "<?php endforeach; ?>";
	}

	/**
	 * Compile the end-if statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndif(?string $expression): string
	{
		return "<?php endif; ?>";
	}

	/**
	 * Compile the end-for-else statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEndforelse(?string $expression): string
	{
		return "<?php endif; ?>";
	}

	/**
	 * Compile the extends statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileExtends(?string $expression): string
	{
		if (starts_with($expression, '('))
		{
			$expression = substr($expression, 1, -1);
		}

		$data = "<?php echo \$this->loadAnyTemplate($expression); ?>";

		$this->footer[] = $data;

		return '';
	}

	/**
	 * Compile the include statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileInclude(?string $expression): string
	{
		if (starts_with($expression, '('))
		{
			$expression = substr($expression, 1, -1);
		}

		return "<?php echo \$this->loadAnyTemplate($expression); ?>";
	}

	/**
	 * Compile the jlayout statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileJlayout(?string $expression): string
	{
		if (starts_with($expression, '('))
		{
			$expression = substr($expression, 1, -1);
		}

		return "<?php echo \\FOF40\\Layout\\LayoutHelper::render(\$this->container, $expression); ?>";
	}

	/**
	 * Compile the stack statements into the content
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileStack(?string $expression): string
	{
		return "<?php echo \$this->yieldContent{$expression}; ?>";
	}

	/**
	 * Compile the push statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compilePush(?string $expression): string
	{
		return "<?php \$this->startSection{$expression}; ?>";
	}

	/**
	 * Compile the endpush statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return  string
	 */
	protected function compileEndpush(?string $expression): string
	{
		return "<?php \$this->appendSection(); ?>";
	}

	/**
	 * Compile the route statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileRoute(?string $expression): string
	{
		return "<?php echo \$this->container->template->route{$expression}; ?>";
	}

	/**
	 * Compile the css statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileCss(?string $expression): string
	{
		return "<?php \$this->addCssFile{$expression}; ?>";
	}

	/**
	 * Compile the inlineCss statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileInlineCss(?string $expression): string
	{
		return "<?php \$this->addCssInline{$expression}; ?>";
	}

	/**
	 * Compile the inlineJs statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileInlineJs(?string $expression): string
	{
		return "<?php \$this->addJavascriptInline{$expression}; ?>";
	}

	/**
	 * Compile the js statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileJs(?string $expression): string
	{
		return "<?php \$this->addJavascriptFile{$expression}; ?>";
	}

	/**
	 * Compile the jhtml statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileJhtml(?string $expression): string
	{
		return "<?php echo \\Joomla\\CMS\\HTML\\HTMLHelper::_{$expression}; ?>";
	}

	/**
	 * Compile the `sortgrid` statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 *
	 * @since 3.3.0
	 */
	protected function compileSortgrid(?string $expression): string
	{
		return "<?php echo \FOF40\Html\FEFHelper\BrowseView::sortGrid{$expression} ?>";
	}

	/**
	 * Compile the `fieldtitle` statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 *
	 * @since 3.3.0
	 */
	protected function compileFieldtitle(?string $expression): string
	{
		return "<?php echo \FOF40\Html\FEFHelper\BrowseView::fieldLabel{$expression} ?>";
	}

	/**
	 * Compile the `modelfilter($localField, [$modelTitleField, $modelName, $placeholder, $params])` statements into
	 * valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 *
	 * @since 3.3.0
	 */
	protected function compileModelfilter(?string $expression): string
	{
		return "<?php echo \FOF40\Html\FEFHelper\BrowseView::modelFilter{$expression} ?>";
	}

	/**
	 * Compile the `selectfilter($localField, $options [, $placeholder, $params])` statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 *
	 * @since 3.3.0
	 */
	protected function compileSelectfilter(?string $expression): string
	{
		return "<?php echo \FOF40\Html\FEFHelper\BrowseView::selectFilter{$expression} ?>";
	}

	/**
	 * Compile the `searchfilter($localField, $searchField = null, $placeholder = null, array $attributes = [])`
	 * statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 *
	 * @since 3.3.0
	 */
	protected function compileSearchfilter(?string $expression): string
	{
		return "<?php echo \FOF40\Html\FEFHelper\BrowseView::searchFilter{$expression} ?>";
	}

	/**
	 * Compile the media statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileMedia(?string $expression): string
	{
		return "<?php echo \$this->container->template->parsePath{$expression}; ?>";
	}

	/**
	 * Compile the modules statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileModules(?string $expression): string
	{
		return "<?php echo \$this->container->template->loadPosition{$expression}; ?>";
	}

	/**
	 * Compile the module statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileModule(?string $expression): string
	{
		return "<?php echo \$this->container->template->loadModule{$expression}; ?>";
	}

	/**
	 * Compile the editor statements into valid PHP.
	 *
	 * @param   string  $expression
	 *
	 * @return string
	 */
	protected function compileEditor(?string $expression): string
	{
		return '<?php echo \\Joomla\\CMS\\Editor\\Editor::getInstance($this->container->platform->getConfig()->get(\'editor\', \'tinymce\'))' .
			'->display' . $expression . '; ?>';
	}

	/**
	 * Gets the tags used for the compiler.
	 *
	 * @param   bool  $escaped
	 *
	 * @return array
	 */
	protected function getTags(bool $escaped = false): array
	{
		$tags = $escaped ? $this->escapedTags : $this->contentTags;

		return array_map('stripcslashes', $tags);
	}

}
ViewTemplateFinder.php000060400000041243152455612700011022 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\View;

defined('_JEXEC') || die;

use FOF40\Container\Container;
use Joomla\CMS\Language\Text;
use RuntimeException;

/**
 * Locates the appropriate template file for a view
 */
class ViewTemplateFinder
{
	/** @var  View  The view we are attached to */
	protected $view;

	/** @var  Container  The container of the view, for quick reference */
	protected $container;

	/** @var  array  The layout template extensions to look for */
	protected $extensions = array('.blade.php', '.php');

	/** @var  string  Default layout's name (default: "default") */
	protected $defaultLayout = 'default';

	/** @var  string  Default sub-template name (default: empty) */
	protected $defaultTpl = '';

	/** @var  bool  Should I only look in the specified view (true) or also the pluralised/singularised (false) */
	protected $strictView = true;

	/** @var  bool  Should I only look for the defined sub-template or also no sub-template? */
	protected $strictTpl = true;

	/** @var  bool  Should  Should I only look for this layout or also the default layout? */
	protected $strictLayout = true;

	/** @var  string  Which application side prefix should I use by default (site, admin, auto, any) */
	protected $sidePrefix = 'auto';

	/**
	 * Public constructor. The config array can contain the following keys
	 * extensions       array
	 * defaultLayout    string
	 * defaultTpl       string
	 * strictView       bool
	 * strictTpl        bool
	 * strictLayout     bool
	 * sidePrefix       string
	 * For the descriptions of each key please see the same-named property of this class
	 *
	 * @param   View   $view    The view we are attached to
	 * @param   array  $config  The configuration for this view template finder
	 */
	function __construct(View $view, array $config = array())
	{
		$this->view = $view;
		$this->container = $view->getContainer();

		if (isset($config['extensions']))
		{
			if (!is_array($config['extensions']))
			{
				$config['extensions'] = trim($config['extensions']);
				$config['extensions'] = explode(',', $config['extensions']);
				$config['extensions'] = array_map(function ($x) { return trim($x); }, $config['extensions']);
			}

			$this->setExtensions($config['extensions']);
		}

		if (isset($config['defaultLayout']))
		{
			$this->setDefaultLayout($config['defaultLayout']);
		}

		if (isset($config['defaultTpl']))
		{
			$this->setDefaultTpl($config['defaultTpl']);
		}

		if (isset($config['strictView']))
		{
			$config['strictView'] = in_array($config['strictView'], array(true, 'true', 'yes', 'on', 1));

			$this->setStrictView($config['strictView']);
		}

		if (isset($config['strictTpl']))
		{
			$config['strictTpl'] = in_array($config['strictTpl'], array(true, 'true', 'yes', 'on', 1));

			$this->setStrictTpl($config['strictTpl']);
		}

		if (isset($config['strictLayout']))
		{
			$config['strictLayout'] = in_array($config['strictLayout'], array(true, 'true', 'yes', 'on', 1));

			$this->setStrictLayout($config['strictLayout']);
		}

		if (isset($config['sidePrefix']))
		{
			$this->setSidePrefix($config['sidePrefix']);
		}
	}

	/**
	 * Returns a list of template URIs for a specific component, view, template and sub-template. The $parameters array
	 * can have any of the following keys:
	 * component        string  The name of the component, e.g. com_something
	 * view             string  The name of the view
	 * layout           string  The name of the layout
	 * tpl              string  The name of the sub-template
	 * strictView       bool    Should I only look in the specified view, or should I look in the pluralised/singularised view as well?
	 * strictLayout     bool    Should I only look for this layout, or also for the default layout?
	 * strictTpl        bool    Should I only look for this sub-template or also for no sub-template?
	 * sidePrefix       string  The application side prefix (site, admin, auto, any)
	 *
	 * @param   array  $parameters  See above
	 *
	 * @return  array
	 * @throws  \Exception
	 */
	public function getViewTemplateUris(array $parameters)
	{
		// Merge the default parameters with the parameters given
		$parameters = array_merge(array(
			'component'     => $this->container->componentName,
			'view'          => $this->view->getName(),
			'layout'        => $this->defaultLayout,
			'tpl'           => $this->defaultTpl,
			'strictView'    => $this->strictView,
			'strictLayout'  => $this->strictLayout,
			'strictTpl'     => $this->strictTpl,
			'sidePrefix'    => $this->sidePrefix,
		), $parameters);

		$uris = array();

		$component       = $parameters['component'];
		$view            = $parameters['view'];
		$layout          = $parameters['layout'];
		$tpl             = $parameters['tpl'];
		$strictView      = $parameters['strictView'];
		$strictLayout    = $parameters['strictLayout'];
		$strictTpl       = $parameters['strictTpl'];
		$sidePrefix      = $parameters['sidePrefix'];

		$basePath = $sidePrefix.  ':' . $component . '/' . $view . '/';

		$uris[] = $basePath . $layout . ($tpl ? "_$tpl" : '');

		if (!$strictTpl)
		{
			$uris[] = $basePath . $layout;
		}

		if (!$strictLayout)
		{
			$uris[] = $basePath . 'default' . ($tpl ? "_$tpl" : '');

			if (!$strictTpl)
			{
				$uris[] = $basePath . 'default';
			}
		}

		if (!$strictView)
		{
			$parameters['view'] = $this->container->inflector->isSingular($view) ? $this->container->inflector->pluralize($view) : $this->container->inflector->singularize($view);
			$parameters['strictView'] = true;

			$extraUris = $this->getViewTemplateUris($parameters);
			$uris = array_merge($uris, $extraUris);
			unset ($extraUris);
		}

		return array_unique($uris);
	}

	/**
	 * Parses a template URI in the form of admin:component/view/layout to an array listing the application section
	 * (admin, site), component, view and template referenced therein.
	 *
	 * @param   string  $uri  The template path to parse
	 *
	 * @return  array  A hash array with the parsed path parts. Keys: admin, component, view, template
	 */
	public function parseTemplateUri($uri = '')
	{
		$parts = array(
			'admin'		 => 0,
			'component'	 => $this->container->componentName,
			'view'		 => $this->view->getName(),
			'template'	 => 'default'
		);

		if (substr($uri, 0, 5) == 'auto:')
		{
			$replacement = $this->container->platform->isBackend() ? 'admin:' : 'site:';
			$uri = $replacement . substr($uri, 5);
		}

		if (substr($uri, 0, 6) == 'admin:')
		{
			$parts['admin'] = 1;
			$uri = substr($uri, 6);
		}
		elseif (substr($uri, 0, 5) == 'site:')
		{
			$uri = substr($uri, 5);
		}
		elseif (substr($uri, 0, 4) == 'any:')
		{
			$parts['admin'] = -1;
			$uri = substr($uri, 4);
		}

		if (empty($uri))
		{
			return $parts;
		}

		$uriParts = explode('/', $uri, 3);
		$partCount = count($uriParts);

		if ($partCount >= 1)
		{
			$parts['component'] = $uriParts[0];
		}

		if ($partCount >= 2)
		{
			$parts['view'] = $uriParts[1];
		}

		if ($partCount >= 3)
		{
			$parts['template'] = $uriParts[2];
		}

		return $parts;
	}

	/**
	 * Resolves a view template URI (e.g. any:com_foobar/Items/cheese) to an absolute filesystem path
	 * (e.g. /var/www/html/administrator/components/com_foobar/View/Items/tmpl/cheese.php)
	 *
	 * @param   string  $uri             The view template URI to parse
	 * @param   string  $layoutTemplate  The layout template override of the View class
	 * @param   array   $extraPaths      Any extra lookup paths where we'll be looking for this view template
	 * @param   bool    $noOverride      If true we will not load Joomla! template overrides. Useful when you want the
	 *                                   template overrides to extend the original view template.
	 *
	 * @return  string
	 *
	 */
	public function resolveUriToPath(string $uri, string $layoutTemplate = '', array $extraPaths = [], bool $noOverride = false)
	{
		// Parse the URI into its parts
		$parts = $this->parseTemplateUri($uri);

		// Get some useful values
		$isAdmin        = $this->container->platform->isBackend() ? 1 : 0;
		$componentPaths = $this->container->platform->getComponentBaseDirs($parts['component']);
		$templatePath   = $this->container->platform->getTemplateOverridePath($parts['component']);

		// Get the lookup paths
		$paths = [];

		// If we are on the correct side of the application or we have an "any:" URI look for a template override
		if (!$noOverride && (($parts['admin'] == -1) || ($parts['admin'] == $isAdmin)))
		{
			$paths[] = $templatePath . '/' . $parts['view'];
		}

		// Add the requested side of the application
		$requestedAdmin = ($parts['admin'] == -1) ? $isAdmin : $parts['admin'];

		$paths[] = ($requestedAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/tmpl/' . $parts['view'];
		$paths[] = ($requestedAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/ViewTemplates/' . $parts['view'];

		// Add the other side of the application for "any:" URIs
		if ($parts['admin'] == -1)
		{
			$paths[] = ($requestedAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/tmpl/' . $parts['view'];
			$paths[] = ($requestedAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/ViewTemplates/' . $parts['view'];
		}

		// Add extra paths
		if (!empty($extraPaths))
		{
			$paths = array_merge($paths, $extraPaths);
		}

		// Remove duplicate paths
		$paths = array_unique($paths);

		// Look for a template layout override
		if (!empty($layoutTemplate) && ($layoutTemplate != '_') && ($layoutTemplate != $parts['template']))
		{
			$apath = array_shift($paths);
			array_unshift($paths, str_replace($parts['template'], $layoutTemplate, $apath));
		}

		// Get the Joomla! version template suffixes
		$jVersionSuffixes = array_merge($this->container->platform->getTemplateSuffixes(), ['']);

		// Get the renderer name suffixes
		$rendererNameSuffixes = [
			'.' . $this->container->renderer->getInformation()->name,
			'',
		];

		$filesystem = $this->container->filesystem;

		// Try to find a view template in the component specified (or its view override)
		foreach ($this->extensions as $extension)
		{
			foreach ($jVersionSuffixes as $JVersionSuffix)
			{
				foreach ($rendererNameSuffixes as $rendererNameSuffix)
				{
					$filenameToFind = $parts['template'] . $JVersionSuffix . $rendererNameSuffix . $extension;

					$fileName = $filesystem->pathFind($paths, $filenameToFind);

					if (!is_null($fileName))
					{
						return $fileName;
					}
				}
			}
		}

		/**
		 * If no view template was found for the component fall back to FOF's core Blade templates -- located in
		 * <libdir>/ViewTemplates/<viewName>/<templateName> -- and their template overrides.
		 */
		$paths   = [];
		$paths[] = $this->container->platform->getTemplateOverridePath('lib_fof40') . '/' . $parts['view'];
		$paths[] = realpath(__DIR__ . '/..') . '/ViewTemplates/' . $parts['view'];

		foreach ($jVersionSuffixes as $JVersionSuffix)
		{
			foreach ($rendererNameSuffixes as $rendererNameSuffix)
			{
				$filenameToFind = $parts['template'] . $JVersionSuffix . $rendererNameSuffix . '.blade.php';

				$fileName = $filesystem->pathFind($paths, $filenameToFind);

				if (!is_null($fileName))
				{
					return $fileName;
				}
			}
		}

		// Nothing found, throw an error
		throw new RuntimeException(Text::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $uri), 500);
	}

	/**
	 * Get the list of view template extensions
	 *
	 * @return  array
	 *
	 * @codeCoverageIgnore
	 */
	public function getExtensions()
	{
		return $this->extensions;
	}

	/**
	 * Set the list of view template extensions
	 *
	 * @param   array  $extensions
	 *
	 * @return  void
	 *
	 * @codeCoverageIgnore
	 */
	public function setExtensions(array $extensions)
	{
		$this->extensions = $extensions;
	}

	/**
	 * Add an extension to the list of view template extensions
	 *
	 * @param   string  $extension
	 *
	 * @return  void
	 */
	public function addExtension($extension)
	{
		if (empty($extension))
		{
			return;
		}

		if (substr($extension, 0, 1) != '.')
		{
			$extension = '.' . $extension;
		}

		if (!in_array($extension, $this->extensions))
		{
			$this->extensions[] = $extension;
		}
	}

	/**
	 * Remove an extension from the list of view template extensions
	 *
	 * @param   string  $extension
	 *
	 * @return  void
	 */
	public function removeExtension($extension)
	{
		if (empty($extension))
		{
			return;
		}

		if (substr($extension, 0, 1) != '.')
		{
			$extension = '.' . $extension;
		}

		if (!in_array($extension, $this->extensions))
		{
			return;
		}

		$pos = array_search($extension, $this->extensions);
		unset ($this->extensions[$pos]);
	}

	/**
	 * Returns the default layout name
	 *
	 * @return  string
	 *
	 * @codeCoverageIgnore
	 */
	public function getDefaultLayout()
	{
		return $this->defaultLayout;
	}

	/**
	 * Sets the default layout name
	 *
	 * @param   string  $defaultLayout
	 *
	 * @return  void
	 */
	public function setDefaultLayout($defaultLayout)
	{
		$this->defaultLayout = $defaultLayout;
	}

	/**
	 * Returns the default sub-template name
	 *
	 * @return  string
	 *
	 * @codeCoverageIgnore
	 */
	public function getDefaultTpl()
	{
		return $this->defaultTpl;
	}

	/**
	 * Sets the default sub-template name
	 *
	 * @param  string  $defaultTpl
	 *
	 * @codeCoverageIgnore
	 */
	public function setDefaultTpl($defaultTpl)
	{
		$this->defaultTpl = $defaultTpl;
	}

	/**
	 * Returns the "strict view" flag. When the flag is false we will look for the view template in both the
	 * singularised and pluralised view. If it's true we will only look for the view template in the view
	 * specified in getViewTemplateUris.
	 *
	 * @return  boolean
	 *
	 * @codeCoverageIgnore
	 */
	public function isStrictView()
	{
		return $this->strictView;
	}

	/**
	 * Sets the "strict view" flag. When the flag is false we will look for the view template in both the
	 * singularised and pluralised view. If it's true we will only look for the view template in the view
	 * specified in getViewTemplateUris.
	 *
	 * @param   boolean  $strictView
	 *
	 * @return  void
	 *
	 * @codeCoverageIgnore
	 */
	public function setStrictView($strictView)
	{
		$this->strictView = $strictView;
	}

	/**
	 * Returns the "strict template" flag. When the flag is false we will look for a view template with or without the
	 * sub-template defined in getViewTemplateUris. If it's true we will only look for the sub-template specified.
	 *
	 * @return boolean
	 *
	 * @codeCoverageIgnore
	 */
	public function isStrictTpl()
	{
		return $this->strictTpl;
	}

	/**
	 * Sets the "strict template" flag. When the flag is false we will look for a view template with or without the
	 * sub-template defined in getViewTemplateUris. If it's true we will only look for the sub-template specified.
	 *
	 * @param   boolean  $strictTpl
	 *
	 * @return  void
	 *
	 * @codeCoverageIgnore
	 */
	public function setStrictTpl($strictTpl)
	{
		$this->strictTpl = $strictTpl;
	}

	/**
	 * Returns the "strict layout" flag. When the flag is false we will look for a view template with both the specified
	 * and the default template name in getViewTemplateUris. When true we will only look for the specified view
	 * template.
	 *
	 * @return  boolean
	 *
	 * @codeCoverageIgnore
	 */
	public function isStrictLayout()
	{
		return $this->strictLayout;
	}

	/**
	 * Sets the "strict layout" flag. When the flag is false we will look for a view template with both the specified
	 * and the default template name in getViewTemplateUris. When true we will only look for the specified view
	 * template.
	 *
	 * @param   boolean  $strictLayout
	 *
	 * @return  void
	 *
	 * @codeCoverageIgnore
	 */
	public function setStrictLayout($strictLayout)
	{
		$this->strictLayout = $strictLayout;
	}

	/**
	 * Returns the application side prefix which will be used by default in getViewTemplateUris. It can be one of:
	 * site     Public front-end
	 * admin    Administrator back-end
	 * auto     Automatically figure out if it should be site or admin
	 * any      First look in the current application side, then look on the other side of the application
	 *
	 * @return  string
	 *
	 * @codeCoverageIgnore
	 */
	public function getSidePrefix()
	{
		return $this->sidePrefix;
	}

	/**
	 * Sets the application side prefix which will be used by default in getViewTemplateUris. It can be one of:
	 * site     Public front-end
	 * admin    Administrator back-end
	 * auto     Automatically figure out if it should be site or admin
	 * any      First look in the current application side, then look on the other side of the application
	 *
	 * @param   string  $sidePrefix
	 *
	 * @return  void
	 */
	public function setSidePrefix($sidePrefix)
	{
		$sidePrefix = strtolower($sidePrefix);
		$sidePrefix = trim($sidePrefix);

		if (!in_array($sidePrefix, array('site', 'admin', 'auto', 'any')))
		{
			$sidePrefix = 'auto';
		}

		$this->sidePrefix = $sidePrefix;
	}


}
View.php000060400000102240152455612700006171 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View;

defined('_JEXEC') || die;

use FOF40\Container\Container;
use FOF40\Model\Model;
use FOF40\View\Engine\EngineInterface;
use FOF40\View\Engine\PhpEngine;
use FOF40\View\Exception\CannotGetName;
use FOF40\View\Exception\EmptyStack;
use FOF40\View\Exception\ModelNotFound;
use FOF40\View\Exception\UnrecognisedExtension;
use Joomla\CMS\Language\Text;

/**
 * Class View
 *
 * A generic MVC view implementation
 *
 * @property-read  \FOF40\Input\Input $input  The input object (magic __get returns the Input from the Container)
 */
class View
{
	public $baseurl;

	/**
	 * Current or most recently performed task.
	 * Currently public, it should be reduced to protected in the future
	 *
	 * @var  string
	 */
	public $task;

	/**
	 * The mapped task that was performed.
	 * Currently public, it should be reduced to protected in the future
	 *
	 * @var  string
	 */
	public $doTask;

	/**
	 * The name of the view
	 *
	 * @var    array
	 */
	protected $name;

	/**
	 * Registered models
	 *
	 * @var    array
	 */
	protected $modelInstances = [];

	/**
	 * The default model
	 *
	 * @var    string
	 */
	protected $defaultModel;

	/**
	 * Layout name
	 *
	 * @var    string
	 */
	protected $layout = 'default';

	/**
	 * Layout template
	 *
	 * @var    string
	 */
	protected $layoutTemplate = '_';

	/**
	 * The set of search directories for view templates
	 *
	 * @var   array
	 */
	protected $templatePaths = [];

	/**
	 * The name of the default template source file.
	 *
	 * @var   string
	 */
	protected $template;

	/**
	 * The output of the template script.
	 *
	 * @var   string
	 */
	protected $output;

	/**
	 * A cached copy of the configuration
	 *
	 * @var   array
	 */
	protected $config = [];

	/**
	 * The container attached to this view
	 *
	 * @var   Container
	 */
	protected $container;

	/**
	 * The object used to locate view templates in the filesystem
	 *
	 * @var   ViewTemplateFinder
	 */
	protected $viewFinder;

	/**
	 * Used when loading template files to avoid variable scope issues
	 *
	 * @var   null
	 */
	protected $_tempFilePath;

	/**
	 * Should I run the pre-render step?
	 *
	 * @var    boolean
	 */
	protected $doPreRender = true;

	/**
	 * Should I run the post-render step?
	 *
	 * @var    boolean
	 */
	protected $doPostRender = true;

	/**
	 * Maps view template extensions to view engine classes
	 *
	 * @var    array
	 */
	protected $viewEngineMap = [
		'.blade.php' => 'FOF40\\View\\Engine\\BladeEngine',
		'.php'       => 'FOF40\\View\\Engine\\PhpEngine',
	];

	/**
	 * All of the finished, captured sections.
	 *
	 * @var array
	 */
	protected $sections = [];

	/**
	 * The stack of in-progress sections.
	 *
	 * @var array
	 */
	protected $sectionStack = [];

	/**
	 * The number of active rendering operations.
	 *
	 * @var int
	 */
	protected $renderCount = 0;

	/**
	 * Aliases of view templates. For example:
	 *
	 * array('userProfile' => 'site://com_foobar/users/profile')
	 *
	 * allows you to do something like $this->loadAnyTemplate('userProfile') to display the frontend view template
	 * site://com_foobar/users/profile. You can also alias one view template with another, e.g.
	 * 'site://com_something/users/profile' => 'admin://com_foobar/clients/record'
	 *
	 * @var  array
	 */
	protected $viewTemplateAliases = [];

	/**
	 * Constructor.
	 *
	 * The $config array can contain the following overrides:
	 * name           string  The name of the view (defaults to the view class name)
	 * template_path  string  The path of the layout directory
	 * layout         string  The layout for displaying the view
	 * viewFinder     ViewTemplateFinder  The object used to locate view templates in the filesystem
	 * viewEngineMap  array   Maps view template extensions to view engine classes
	 *
	 * @param   Container  $container  The container we belong to
	 * @param   array      $config     The configuration overrides for the view
	 *
	 * @return  View
	 */
	public function __construct(Container $container, array $config = [])
	{
		$this->container = $container;

		$this->config = $config;

		// Get the view name
		if (isset($this->config['name']))
		{
			$this->name = $this->config['name'];
		}

		$this->name = $this->getName();

		// Set the default template search path
		if (array_key_exists('template_path', $this->config))
		{
			// User-defined dirs
			$this->setTemplatePath($this->config['template_path']);
		}
		else
		{
			$this->setTemplatePath($this->container->thisPath . '/View/' . ucfirst($this->name) . '/tmpl');
		}

		// Set the layout
		if (array_key_exists('layout', $this->config))
		{
			$this->setLayout($this->config['layout']);
		}

		// Apply the viewEngineMap
		if (isset($config['viewEngineMap']))
		{
			// If the overrides are a string convert it to an array first.
			if (!is_array($config['viewEngineMap']))
			{
				$temp                    = explode(',', $config['viewEngineMap']);
				$config['viewEngineMap'] = [];

				foreach ($temp as $assignment)
				{
					$parts = explode('=>', $assignment, 2);

					if (count($parts) != 2)
					{
						continue;
					}

					$parts = array_map(function ($x) {
						return trim($x);
					}, $parts);

					$config['viewEngineMap'][$parts[0]] = $parts[1];
				}
			}

			/**
			 * We want to always have a sane fallback to plain .php template files at the end of the view engine stack.
			 * For this to happen to we need to remove it from the current stack, disallow overriding it in the
			 * viewEngineMap overrides, merge the overrides and then add the fallback at the very end of the stack.
			 *
			 * In previous versions we didn't do that which had two side effects:
			 * 1. It was no longer a fallback, it was in the middle of the stack
			 * 2. Any new template engines using a .something.php extension wouldn't work, see
			 *    https://github.com/akeeba/fof/issues/694
			 */

			// Do not allow overriding the fallback .php handler
			if (isset($config['viewEngineMap']['.php']))
			{
				unset ($config['viewEngineMap']['.php']);
			}

			// Temporarily remove the fallback .php handler
			$phpHandler = $this->viewEngineMap['.php'] ?? PhpEngine::class;
			unset ($this->viewEngineMap['.php']);

			// Add the overrides
			$this->viewEngineMap = array_merge($this->viewEngineMap, $config['viewEngineMap']);

			// Push the fallback .php handler to the end of the view engine map stack
			$this->viewEngineMap = array_merge($this->viewEngineMap, [
				'.php' => $phpHandler
			]);
		}

		// Set the ViewFinder
		$this->viewFinder = $this->container->factory->viewFinder($this);

		if (isset($config['viewFinder']) && !empty($config['viewFinder']) && is_object($config['viewFinder']) && ($config['viewFinder'] instanceof ViewTemplateFinder))
		{
			$this->viewFinder = $config['viewFinder'];
		}

		// Apply the registered view template extensions to the view finder
		$this->viewFinder->setExtensions(array_keys($this->viewEngineMap));

		// Apply the base URL
		$this->baseurl = $this->container->platform->URIbase();
	}

	/**
	 * Magic get method. Handles magic properties:
	 * $this->input  mapped to $this->container->input
	 *
	 * @param   string  $name  The property to fetch
	 *
	 * @return  mixed|null
	 */
	public function __get($name)
	{
		// Handle $this->input
		if ($name == 'input')
		{
			return $this->container->input;
		}

		// Property not found; raise error
		$trace = debug_backtrace();
		trigger_error(
			'Undefined property via __get(): ' . $name .
			' in ' . $trace[0]['file'] .
			' on line ' . $trace[0]['line'],
			E_USER_NOTICE);

		return null;
	}

	/**
	 * Method to get the view name
	 *
	 * The model name by default parsed using the classname, or it can be set
	 * by passing a $config['name'] in the class constructor
	 *
	 * @return  string  The name of the model
	 *
	 * @throws  \Exception
	 */
	public function getName()
	{
		if (empty($this->name))
		{
			$r = null;

			if (!preg_match('/(.*)\\\\View\\\\(.*)\\\\(.*)/i', get_class($this), $r))
			{
				throw new CannotGetName;
			}

			$this->name = $r[2];
		}

		return $this->name;
	}

	/**
	 * Escapes a value for output in a view script.
	 *
	 * @param   mixed  $var  The output to escape.
	 *
	 * @return  mixed  The escaped value.
	 */
	public function escape($var)
	{
		return htmlspecialchars($var, ENT_COMPAT, 'UTF-8');
	}

	/**
	 * Method to get data from a registered model or a property of the view
	 *
	 * @param   string  $property   The name of the method to call on the Model or the property to get
	 * @param   string  $default    The default value [optional]
	 * @param   string  $modelName  The name of the Model to reference [optional]
	 *
	 * @return  mixed  The return value of the method
	 */
	public function get($property, $default = null, $modelName = null)
	{
		$model = is_null($modelName) ? $this->defaultModel : $modelName;

		// First check to make sure the model requested exists
		if (isset($this->modelInstances[$model]))
		{
			// Model exists, let's build the method name
			$method = 'get' . ucfirst($property);

			// Does the method exist?
			if (method_exists($this->modelInstances[$model], $method))
			{
				// The method exists, let's call it and return what we get
				$result = $this->modelInstances[$model]->$method();

				return $result;
			}

			$result = $this->modelInstances[$model]->$property();

			if (is_null($result))
			{
				return $default;
			}

			return $result;
		}

		if (@isset($this->$property))
		{
			return $this->$property;
		}

		return $default;
	}

	/**
	 * Returns a named Model object
	 *
	 * @param   string  $name    The Model name. If null we'll use the modelName
	 *                           variable or, if it's empty, the same name as
	 *                           the Controller
	 *
	 * @return  Model  The instance of the Model known to this Controller
	 */
	public function getModel($name = null)
	{
		if (!empty($name))
		{
			$modelName = $name;
		}
		elseif (!empty($this->defaultModel))
		{
			$modelName = $this->defaultModel;
		}
		else
		{
			$modelName = $this->name;
		}

		if (!array_key_exists($modelName, $this->modelInstances))
		{
			throw new ModelNotFound($modelName ?? '', $this->name ?? '');
		}

		return $this->modelInstances[$modelName];
	}

	/**
	 * Pushes the default Model to the View
	 *
	 * @param   Model  $model  The model to push
	 */
	public function setDefaultModel(Model &$model)
	{
		$name = $model->getName();

		$this->setDefaultModelName($name);
		$this->setModel($this->defaultModel, $model);
	}

	/**
	 * Set the name of the Model to be used by this View
	 *
	 * @param   string  $modelName  The name of the Model
	 *
	 * @return  void
	 */
	public function setDefaultModelName($modelName)
	{
		$this->defaultModel = $modelName;
	}

	/**
	 * Pushes a named model to the View
	 *
	 * @param   string  $modelName  The name of the Model
	 * @param   Model   $model      The actual Model object to push
	 *
	 * @return  void
	 */
	public function setModel($modelName, Model &$model)
	{
		$this->modelInstances[$modelName] = $model;
	}

	/**
	 * Overrides the default method to execute and display a template script.
	 * Instead of loadTemplate is uses loadAnyTemplate.
	 *
	 * @param   string  $tpl  The name of the template file to parse
	 *
	 * @return  boolean  True on success
	 *
	 * @throws  \Exception  When the layout file is not found
	 */
	public function display($tpl = null)
	{
		$this->initialise();

		$eventName = 'onBefore' . ucfirst($this->doTask);
		$this->triggerEvent($eventName, [$tpl]);

		$preRenderResult = '';

		if ($this->doPreRender)
		{
			@ob_start();
			$this->preRender();
			$preRenderResult = @ob_get_contents();
			@ob_end_clean();
		}

		$templateResult = $this->loadTemplate($tpl);

		$eventName = 'onAfter' . ucfirst($this->doTask);
		$this->triggerEvent($eventName, [$tpl]);

		if (is_object($templateResult) && ($templateResult instanceof \Exception))
		{
			throw $templateResult;
		}

		echo $preRenderResult . $templateResult;

		if ($this->doPostRender)
		{
			$this->postRender();
		}

		return true;
	}

	/**
	 * Get the layout.
	 *
	 * @return  string  The layout name
	 */
	public function getLayout()
	{
		return $this->layout;
	}

	/**
	 * Sets the layout name to use
	 *
	 * @param   string  $layout  The layout name or a string in format <template>:<layout file>
	 *
	 * @return  string  Previous value.
	 */
	public function setLayout($layout)
	{
		$previous = $this->layout;

		if (is_null($layout))
		{
			$layout = 'default';
		}

		if (strpos($layout, ':') === false)
		{
			$this->layout = $layout;
		}
		else
		{
			// Convert parameter to array based on :
			$temp         = explode(':', $layout);
			$this->layout = $temp[1];

			// Set layout template
			$this->layoutTemplate = $temp[0];
		}

		return $previous;
	}

	/**
	 * Our function uses loadAnyTemplate to provide smarter view template loading.
	 *
	 * @param   string   $tpl     The name of the template file to parse
	 * @param   boolean  $strict  Should we use strict naming, i.e. force a non-empty $tpl?
	 *
	 * @return  mixed  A string if successful, otherwise an Exception
	 */
	public function loadTemplate($tpl = null, $strict = false)
	{
		$result = '';

		$uris = $this->viewFinder->getViewTemplateUris([
			'component' => $this->container->componentName,
			'view'      => $this->getName(),
			'layout'    => $this->getLayout(),
			'tpl'       => $tpl,
			'strictTpl' => $strict,
		]);

		foreach ($uris as $uri)
		{
			try
			{
				$result = $this->loadAnyTemplate($uri);

				break;
			}
			catch (\Exception $e)
			{
				$result = $e;
			}
		}

		if ($result instanceof \Exception)
		{
			$this->container->platform->showErrorPage($result);
		}

		return $result;
	}

	/**
	 * Loads a template given any path. The path is in the format componentPart://componentName/viewName/layoutName,
	 * for example
	 * site:com_example/items/default
	 * admin:com_example/items/default_subtemplate
	 * auto:com_example/things/chair
	 * any:com_example/invoices/printpreview
	 *
	 * @param   string         $uri          The template path
	 * @param   array          $forceParams  A hash array of variables to be extracted in the local scope of the
	 *                                       template file
	 * @param   callable|null  $callback     A method to post-process the 3ναluα+3d view template (I use leetspeak here
	 *                                       because of bad quality hosts with broken scanners)
	 * @param   bool           $noOverride   If true we will not load Joomla! template overrides. Useful when you want
	 *                                       the template overrides to extend the original view template.
	 *
	 * @return  string  The output of the template
	 *
	 * @throws  \Exception  When the layout file is not found
	 */
	public function loadAnyTemplate(string $uri = '', array $forceParams = [], ?callable $callback = null, bool $noOverride = false)
	{
		if (isset($this->viewTemplateAliases[$uri]))
		{
			$uri = $this->viewTemplateAliases[$uri];
		}

		$layoutTemplate = $this->getLayoutTemplate();

		$extraPaths = [];

		if (!empty($this->templatePaths))
		{
			$extraPaths = $this->templatePaths;
		}

		// First get the raw view template path
		$path = $this->viewFinder->resolveUriToPath($uri, $layoutTemplate, $extraPaths, $noOverride);

		// Now get the parsed view template path
		$this->_tempFilePath = $this->getEngine($path)->get($path, $forceParams);

		// We will keep track of the amount of views being rendered so we can flush
		// the section after the complete rendering operation is done. This will
		// clear out the sections for any separate views that may be rendered.
		$this->incrementRender();

		// Get the processed template
		$contents = $this->processTemplate($forceParams);

		// Once we've finished rendering the view, we'll decrement the render count
		// so that each sections get flushed out next time a view is created and
		// no old sections are staying around in the memory of an environment.
		$this->decrementRender();

		$response = isset($callback) ? $callback($this, $contents) : null;

		if (!is_null($response))
		{
			$contents = $response;
		}

		// Once we have the contents of the view, we will flush the sections if we are
		// done rendering all views so that there is nothing left hanging over when
		// another view gets rendered in the future by the application developer.
		$this->flushSectionsIfDoneRendering();

		return $contents;
	}

	/**
	 * Increment the rendering counter.
	 *
	 * @return void
	 */
	public function incrementRender()
	{
		$this->renderCount++;
	}

	/**
	 * Decrement the rendering counter.
	 *
	 * @return void
	 */
	public function decrementRender()
	{
		$this->renderCount--;
	}

	/**
	 * Check if there are no active render operations.
	 *
	 * @return bool
	 */
	public function doneRendering()
	{
		return $this->renderCount == 0;
	}

	/**
	 * Go through a data array and render a sub-template against each record (think master-detail views). This is
	 * accessible through Blade templates as @each
	 *
	 * @param   string  $viewTemplate  The view template to use for each subitem, format
	 *                                 componentPart://componentName/viewName/layoutName
	 * @param   array   $data          The array of data you want to render. It can be a DataModel\Collection, array,
	 *                                 ...
	 * @param   string  $eachItemName  How to call each item in the loaded sub-template (passed through $forceParams)
	 * @param   string  $empty         What to display if the array is empty
	 *
	 * @return string
	 */
	public function renderEach($viewTemplate, $data, $eachItemName, $empty = 'raw|')
	{
		$result = '';

		// If is actually data in the array, we will loop through the data and append
		// an instance of the partial view to the final result HTML passing in the
		// iterated value of this data array, allowing the views to access them.
		if (count($data) > 0)
		{
			foreach ($data as $key => $value)
			{
				$data = ['key' => $key, $eachItemName => $value];

				$result .= $this->loadAnyTemplate($viewTemplate, $data);
			}

			return $result;
		}

		if (starts_with($empty, 'raw|'))
		{
			$result = substr($empty, 4);

			return $result;
		}

		if (starts_with($empty, 'text|'))
		{
			$result = Text::_(substr($empty, 5));

			return $result;
		}

		return $this->loadAnyTemplate($empty);
	}

	/**
	 * Start injecting content into a section.
	 *
	 * @param   string  $section
	 * @param   string  $content
	 *
	 * @return void
	 */
	public function startSection($section, $content = '')
	{
		if ($content === '')
		{
			if (ob_start())
			{
				$this->sectionStack[] = $section;
			}
		}
		else
		{
			$this->extendSection($section, $content);
		}
	}

	/**
	 * Stop injecting content into a section and return its contents.
	 *
	 * @return string
	 */
	public function yieldSection()
	{
		return $this->yieldContent($this->stopSection());
	}

	/**
	 * Stop injecting content into a section.
	 *
	 * @param   bool  $overwrite
	 *
	 * @return string
	 */
	public function stopSection($overwrite = false)
	{
		if (empty($this->sectionStack))
		{
			// Let's close the output buffering
			ob_get_clean();

			throw new EmptyStack();
		}

		$last = array_pop($this->sectionStack);

		if ($overwrite)
		{
			$this->sections[$last] = ob_get_clean();
		}
		else
		{
			$this->extendSection($last, ob_get_clean());
		}

		return $last;
	}

	/**
	 * Stop injecting content into a section and append it.
	 *
	 * @return string
	 */
	public function appendSection()
	{
		if (empty($this->sectionStack))
		{
			// Let's close the output buffering
			ob_get_clean();

			throw new EmptyStack();
		}

		$last = array_pop($this->sectionStack);

		if (isset($this->sections[$last]))
		{
			$this->sections[$last] .= ob_get_clean();
		}
		else
		{
			$this->sections[$last] = ob_get_clean();
		}

		return $last;
	}

	/**
	 * Get the string contents of a section.
	 *
	 * @param   string  $section
	 * @param   string  $default
	 *
	 * @return string
	 */
	public function yieldContent($section, $default = '')
	{
		$sectionContent = $default;

		if (isset($this->sections[$section]))
		{
			$sectionContent = $this->sections[$section];
		}

		return str_replace('@parent', '', $sectionContent);
	}

	/**
	 * Flush all of the section contents.
	 *
	 * @return void
	 */
	public function flushSections()
	{
		$this->sections = [];

		$this->sectionStack = [];
	}

	/**
	 * Flush all of the section contents if done rendering.
	 *
	 * @return void
	 */
	public function flushSectionsIfDoneRendering()
	{
		if ($this->doneRendering())
		{
			$this->flushSections();
		}
	}

	/**
	 * Get the layout template.
	 *
	 * @return  string  The layout template name
	 */
	public function getLayoutTemplate()
	{
		return $this->layoutTemplate;
	}

	/**
	 * Load a helper file
	 *
	 * @param   string  $helperClass   The last part of the name of the helper
	 *                                 class.
	 *
	 * @return  void
	 *
	 * @deprecated  3.0  Just use the class in your code. That's what the autoloader is for.
	 */
	public function loadHelper($helperClass = null)
	{
		// Get the helper class name
		$className = '\\' . $this->container->getNamespacePrefix() . 'Helper\\' . ucfirst($helperClass);

		// This trick autoloads the helper class. We can't instantiate it as
		// helpers are (supposed to be) abstract classes with static method
		// interfaces.
		class_exists($className);
	}

	/**
	 * Returns a reference to the container attached to this View
	 *
	 * @return Container
	 */
	public function &getContainer()
	{
		return $this->container;
	}

	public function getTask()
	{
		return $this->task;
	}

	/**
	 * @param   string  $task
	 *
	 * @return  $this   This for chaining
	 */
	public function setTask($task)
	{
		$this->task = $task;

		return $this;
	}

	public function getDoTask()
	{
		return $this->doTask;
	}

	/**
	 * @param   string  $task
	 *
	 * @return  $this   This for chaining
	 */
	public function setDoTask($task)
	{
		$this->doTask = $task;

		return $this;
	}

	/**
	 * Sets the pre-render flag
	 *
	 * @param   boolean  $value  True to enable the pre-render step
	 *
	 * @return  void
	 */
	public function setPreRender($value)
	{
		$this->doPreRender = $value;
	}

	/**
	 * Sets the post-render flag
	 *
	 * @param   boolean  $value  True to enable the post-render step
	 *
	 * @return  void
	 */
	public function setPostRender($value)
	{
		$this->doPostRender = $value;
	}

	/**
	 * Add an alias for a view template.
	 *
	 * @param   string  $viewTemplate  Existing view template, in the format
	 *                                 componentPart://componentName/viewName/layoutName
	 * @param   string  $alias         The alias of the view template (any string will do)
	 *
	 * @return void
	 */
	public function alias($viewTemplate, $alias)
	{
		$this->viewTemplateAliases[$alias] = $viewTemplate;
	}

	/**
	 * Add a JS script file to the page generated by the CMS.
	 *
	 * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp):
	 * * $defer false, $async true: The script is executed asynchronously with the rest of the page
	 *   (the script will be executed while the page continues the parsing)
	 * * $defer true, $async false: The script is executed when the page has finished parsing.
	 * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes
	 *   loading the browser continues parsing the rest of the page.
	 *
	 * When you are using $defer = true there is no guarantee about the load order of the scripts. Whichever
	 * script loads first will be executed first. The order they appear on the page is completely irrelevant.
	 *
	 * @param   string   $uri      A path definition understood by parsePath, e.g. media://com_example/js/foo.js
	 * @param   string   $version  (optional) Version string to be added to the URL
	 * @param   string   $type     MIME type of the script
	 * @param   boolean  $defer    Adds the defer attribute, see above
	 * @param   boolean  $async    Adds the async attribute, see above
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addJavascriptFile($uri, $version = null, $type = 'text/javascript', $defer = false, $async = false)
	{
		// Add an automatic version if $version is null. For no version parameter pass an empty string to $version.
		if (is_null($version))
		{
			$version = $this->container->mediaVersion;
		}

		$this->container->template->addJS($uri, $defer, $async, $version, $type);

		return $this;
	}

	/**
	 * Adds an inline JavaScript script to the page header
	 *
	 * @param   string  $script  The script content to add
	 * @param   string  $type    The MIME type of the script
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addJavascriptInline($script, $type = 'text/javascript')
	{
		$this->container->template->addJSInline($script, $type);

		return $this;
	}

	/**
	 * Add a CSS file to the page generated by the CMS
	 *
	 * @param   string  $uri      A path definition understood by parsePath, e.g. media://com_example/css/foo.css
	 * @param   string  $version  (optional) Version string to be added to the URL
	 * @param   string  $type     MIME type of the stylesheeet
	 * @param   string  $media    Media target definition of the style sheet, e.g. "screen"
	 * @param   array   $attribs  Array of attributes
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addCssFile($uri, $version = null, $type = 'text/css', $media = null, $attribs = [])
	{
		// Add an automatic version if $version is null. For no version parameter pass an empty string to $version.
		if (is_null($version))
		{
			$version = $this->container->mediaVersion;
		}

		$this->container->template->addCSS($uri, $version, $type, $media, $attribs);

		return $this;
	}

	/**
	 * Adds an inline stylesheet (inline CSS) to the page header
	 *
	 * @param   string  $css   The stylesheet content to add
	 * @param   string  $type  The MIME type of the script
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addCssInline($css, $type = 'text/css')
	{
		$this->container->template->addCSSInline($css, $type);

		return $this;
	}

	/**
	 * Sets an entire array of search paths for templates or resources.
	 *
	 * @param   mixed  $path  The new search path, or an array of search paths.  If null or false, resets to the current
	 *                        directory only.
	 *
	 * @return  void
	 */
	protected function setTemplatePath($path)
	{
		// Clear out the prior search dirs
		$this->templatePaths = [];

		// Actually add the user-specified directories
		$this->addTemplatePath($path);

		// Set the alternative template search dir
		$templatePath = JPATH_THEMES;
		$fallback     = $templatePath . '/' . $this->container->platform->getTemplate() . '/html/' . $this->container->componentName . '/' . $this->name;
		$this->addTemplatePath($fallback);

		// Get extra directories through event dispatchers
		$extraPathsResults = $this->container->platform->runPlugins('onGetViewTemplatePaths', [
			$this->container->componentName,
			$this->getName(),
		]);

		if (!is_array($extraPathsResults))
		{
			return;
		}

		if (empty($extraPathsResults))
		{
			return;
		}

		foreach ($extraPathsResults as $somePaths)
		{
			if (!empty($somePaths))
			{
				foreach ($somePaths as $aPath)
				{
					$this->addTemplatePath($aPath);
				}
			}
		}
	}

	/**
	 * Adds to the search path for templates and resources.
	 *
	 * @param   mixed  $path  The directory or stream, or an array of either, to search.
	 *
	 * @return  void
	 */
	protected function addTemplatePath($path)
	{
		// Just force to array
		$path = (array) $path;

		// Loop through the path directories
		foreach ($path as $dir)
		{
			// No surrounding spaces allowed!
			$dir = trim($dir);

			// Add trailing separators as needed
			if (substr($dir, -1) != DIRECTORY_SEPARATOR)
			{
				// Directory
				$dir .= DIRECTORY_SEPARATOR;
			}

			// Add to the top of the search dirs
			array_unshift($this->templatePaths, $dir);
		}
	}

	/**
	 * Append content to a given section.
	 *
	 * @param   string  $section
	 * @param   string  $content
	 *
	 * @return void
	 */
	protected function extendSection($section, $content)
	{
		if (isset($this->sections[$section]))
		{
			$content = str_replace('@parent', $content, $this->sections[$section]);
		}

		$this->sections[$section] = $content;
	}

	/**
	 * Evaluates the template described in the _tempFilePath property
	 *
	 * @param   array  $forceParams  Forced parameters
	 *
	 * @return string
	 * @throws \Exception
	 */
	protected function processTemplate(array &$forceParams)
	{
		// If the engine returned raw content, return the raw content immediately
		if ($this->_tempFilePath['type'] == 'raw')
		{
			return $this->_tempFilePath['content'];
		}

		if (substr($this->_tempFilePath['content'], 0, 4) == 'raw|')
		{
			return substr($this->_tempFilePath['content'], 4);
		}

		$obLevel = ob_get_level();

		ob_start();

		// We'll process the contents of the view inside a try/catch block so we can
		// flush out any stray output that might get out before an error occurs or
		// an exception is thrown. This prevents any partial views from leaking.
		try
		{
			$this->includeTemplateFile($forceParams);
		}
		catch (\Exception $e)
		{
			$this->handleViewException($e, $obLevel);
		}

		return ob_get_clean();
	}

	/**
	 * Handle a view exception.
	 *
	 * @param   \Exception  $e        The exception to handle
	 * @param   int         $obLevel  The target output buffering level
	 *
	 * @return  void
	 *
	 * @throws  $e
	 */
	protected function handleViewException(\Exception $e, $obLevel)
	{
		while (ob_get_level() > $obLevel)
		{
			ob_end_clean();
		}

		$message = $e->getMessage() . ' (View template: ' . realpath($this->_tempFilePath['content']) . ')';

		$newException = new \ErrorException($message, 0, 1, $e->getFile(), $e->getLine(), $e);

		throw $newException;
	}

	/**
	 * Get the appropriate view engine for the given view template path.
	 *
	 * @param   string  $path  The path of the view template
	 *
	 * @return  EngineInterface
	 *
	 * @throws  UnrecognisedExtension
	 */
	protected function getEngine($path)
	{
		foreach ($this->viewEngineMap as $extension => $engine)
		{
			if (substr($path, -strlen($extension)) == $extension)
			{
				return new $engine($this);
			}
		}

		throw new UnrecognisedExtension($path);
	}

	/**
	 * Get the extension used by the view file.
	 *
	 * @param   string  $path
	 *
	 * @return string
	 */
	protected function getExtension($path)
	{
		$extensions = array_keys($this->viewEngineMap);

		return array_first($extensions, function ($key, $value) use ($path) {
			return ends_with($path, $value);
		});
	}

	/**
	 * Triggers an object-specific event. The event runs both locally –if a suitable method exists– and through the
	 * Joomla! plugin system. A true/false return value is expected. The first false return cancels the event.
	 *
	 * EXAMPLE
	 * Component: com_foobar, Object name: item, Event: onBeforeSomething, Arguments: array(123, 456)
	 * The event calls:
	 * 1. $this->onBeforeSomething(123, 456)
	 * 2. Joomla! plugin event onComFoobarViewItemBeforeSomething($this, 123, 456)
	 *
	 * @param   string  $event      The name of the event, typically named onPredicateVerb e.g. onBeforeKick
	 * @param   array   $arguments  The arguments to pass to the event handlers
	 *
	 * @return  bool
	 */
	protected function triggerEvent($event, array $arguments = [])
	{
		$result = true;

		// If there is an object method for this event, call it
		if (method_exists($this, $event))
		{
			$result = $this->{$event}(...$arguments);
		}

		if ($result === false)
		{
			return false;
		}

		// All other event handlers live outside this object, therefore they need to be passed a reference to this
		// objects as the first argument.
		array_unshift($arguments, $this);

		// If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later.
		$prefix = '';

		if (substr($event, 0, 2) == 'on')
		{
			$prefix = 'on';
			$event  = substr($event, 2);
		}

		// Get the component/model prefix for the event
		$prefix .= 'Com' . ucfirst($this->container->bareComponentName) . 'View';
		$prefix .= ucfirst($this->getName());

		// The event name will be something like onComFoobarItemsBeforeSomething
		$event = $prefix . $event;

		// Call the Joomla! plugins
		$results = $this->container->platform->runPlugins($event, $arguments);

		return !in_array(false, $results, true);
	}

	/**
	 * Runs before rendering the view template, echoing HTML to put before the view template's generated HTML.
	 *
	 * This method runs **before** executing the OnBefore* view events.
	 *
	 * @return void
	 */
	protected function initialise(): void
	{

	}

	/**
	 * Runs before rendering the view template, echoing HTML to put before the view template's generated HTML.
	 *
	 * This method runs **after** executing the OnBefore* view events.
	 *
	 * @return void
	 */
	protected function preRender(): void
	{
		// You need to implement this in children classes
	}

	/**
	 * Runs after rendering the view template, echoing HTML to put after the
	 * view template's generated HTML
	 *
	 * @return  void
	 */
	protected function postRender(): void
	{
		// You need to implement this in children classes
	}

	/**
	 * This method makes sure the current scope isn't polluted with variables when including a view template
	 *
	 * @param   array  $forceParams  Forced parameters
	 *
	 * @return  void
	 */
	private function includeTemplateFile(array &$forceParams)
	{
		// Extract forced parameters
		if (!empty($forceParams))
		{
			extract($forceParams);
		}

		include $this->_tempFilePath['content'];
	}
}
DataView/Csv.php000060400000013361152455612700007523 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\DataView;

defined('_JEXEC') or die;

use FOF40\Container\Container;
use FOF40\Model\DataModel;
use FOF40\View\Exception\AccessForbidden;
use Joomla\CMS\Document\Document;

class Csv extends Html implements DataViewInterface
{
	/**
	 *  Should I produce a CSV header row.
	 *
	 * @var  boolean
	 */
	protected $csvHeader = true;

	/**
	 * The filename of the downloaded CSV file.
	 *
	 * @var  string
	 */
	protected $csvFilename;

	/**
	 * The columns to include in the CSV output. If it's empty it will be ignored.
	 *
	 * @var  array
	 */
	protected $csvFields = [];


	/**
	 * Public constructor.
	 *
	 * @param   Container  $container  The container we belong to
	 * @param   array      $config     The configuration overrides for the view
	 */
	public function __construct(Container $container, array $config = [])
	{
		parent::__construct($container, $config);

		if (array_key_exists('csv_header', $config))
		{
			$this->csvHeader = $config['csv_header'];
		}
		else
		{
			$this->csvHeader = $this->input->getBool('csv_header', true);
		}

		if (array_key_exists('csv_filename', $config))
		{
			$this->csvFilename = $config['csv_filename'];
		}
		else
		{
			$this->csvFilename = $this->input->getString('csv_filename', '');
		}

		if (empty($this->csvFilename))
		{
			$view              = $this->input->getCmd('view', 'cpanel');
			$view              = $this->container->inflector->pluralize($view);
			$this->csvFilename = strtolower($view) . '.csv';
		}

		if (array_key_exists('csv_fields', $config))
		{
			$this->csvFields = $config['csv_fields'];
		}
	}

	/**
	 * Overrides the default method to execute and display a template script.
	 * Instead of loadTemplate is uses loadAnyTemplate.
	 *
	 * @param   string  $tpl  The name of the template file to parse
	 *
	 * @return  boolean  True on success
	 *
	 * @throws  \Exception  When the layout file is not found
	 */
	public function display($tpl = null)
	{
		$eventName = 'onBefore' . ucfirst($this->doTask);
		$this->triggerEvent($eventName, [$tpl]);

		// Load the model
		/** @var DataModel $model */
		$model = $this->getModel();

		$items       = $model->get();
		$this->items = $items;

		$platform = $this->container->platform;
		$document = $platform->getDocument();

		if ($document instanceof Document)
		{
			$document->setMimeEncoding('text/csv');
		}

		$platform->setHeader('Pragma', 'public');
		$platform->setHeader('Expires', '0');

		/**
		 * This construct is required to work around bad quality hosts who blacklist files based on broken malware
		 * scanners. The only way to beat them is... wait for it... write our software using the same obscure constructs
		 * actual malware is using to evade these broken malware scanners. The irony is not lost on me.
		 */
		$xo   = substr("revenge", 0, 3);
		$xoxo = substr("calibrate", 1, 2);
		$platform->setHeader('Cache-Control', 'must-' . $xo . $xoxo . 'idate, post-check=0, pre-check=0');

		$platform->setHeader('Cache-Control', 'public', false);
		$platform->setHeader('Content-Description', 'File Transfer');
		$platform->setHeader('Content-Disposition', 'attachment; filename="' . $this->csvFilename . '"');

		if (is_null($tpl))
		{
			$tpl = 'csv';
		}

		$hasFailed = false;

		try
		{
			$result = $this->loadTemplate($tpl, true);

			if ($result instanceof \Exception)
			{
				$hasFailed = true;
			}
		}
		catch (\Exception $e)
		{
			$hasFailed = true;
		}

		if (!$hasFailed)
		{
			echo $result;
		}
		else
		{
			// Default CSV behaviour in case the template isn't there!
			if (count($items) === 0)
			{
				throw new AccessForbidden;
			}

			$item = $items->last();
			$keys = $item->getData();
			$keys = array_keys($keys);

			reset($items);

			if (!empty($this->csvFields))
			{
				$temp = [];

				foreach ($this->csvFields as $f)
				{
					$exist = false;

					// If we have a dot and it isn't part of the field name, we are dealing with relations
					if (!$model->hasField($f) && strpos($f, '.'))
					{
						$methods = explode('.', $f);
						$object  = $item;
						// Let's see if the relation exists
						foreach ($methods as $method)
						{
							if (isset($object->$method) || property_exists($object, $method))
							{
								$exist  = true;
								$object = $object->$method;
							}
							else
							{
								$exist = false;
								break;
							}
						}
					}

					if (in_array($f, $keys))
					{
						$temp[] = $f;
					}
					elseif ($exist)
					{
						$temp[] = $f;
					}
				}

				$keys = $temp;
			}

			if ($this->csvHeader)
			{
				$csv = [];

				foreach ($keys as $k)
				{
					$k = str_replace('"', '""', $k);
					$k = str_replace("\r", '\\r', $k);
					$k = str_replace("\n", '\\n', $k);
					$k = '"' . $k . '"';

					$csv[] = $k;
				}

				echo implode(",", $csv) . "\r\n";
			}

			foreach ($items as $item)
			{
				$csv = [];

				foreach ($keys as $k)
				{
					// If our key contains a dot and it isn't part of the field name, we are dealing with relations
					if (!$model->hasField($k) && strpos($k, '.'))
					{
						$methods = explode('.', $k);
						$v       = $item;

						foreach ($methods as $method)
						{
							$v = $v->$method;
						}
					}
					else
					{
						$v = $item->$k;
					}

					if (is_array($v))
					{
						$v = 'Array';
					}
					elseif (is_object($v))
					{
						$v = 'Object';
					}

					$v = str_replace('"', '""', $v);
					$v = str_replace("\r", '\\r', $v);
					$v = str_replace("\n", '\\n', $v);
					$v = '"' . $v . '"';

					$csv[] = $v;
				}

				echo implode(",", $csv) . "\r\n";
			}
		}

		$eventName = 'onAfter' . ucfirst($this->doTask);
		$this->triggerEvent($eventName, [$tpl]);

		return true;
	}
}
DataView/Raw.php000060400000023647152455612700007531 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\DataView;

defined('_JEXEC') || die;

use FOF40\Container\Container;
use FOF40\Model\DataModel;
use FOF40\Model\DataModel\Collection;
use FOF40\View\View;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Factory as JoomlaFactory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Pagination\Pagination;
use Joomla\Registry\Registry;

/**
 * View for a raw data-driven view
 */
class Raw extends View implements DataViewInterface
{
	/** @var   \stdClass  Data lists */
	protected $lists;

	/** @var Pagination The pagination object */
	protected $pagination;

	/** @var Registry Page parameters object, for front-end views */
	protected $pageParams;

	/** @var Collection The records loaded (browse views) */
	protected $items;

	/** @var DataModel The record loaded (read, edit, add views) */
	protected $item;

	/** @var int The total number of items in the model (more than those loaded) */
	protected $itemCount = 0;

	/** @var \stdClass ACL permissions map */
	protected $permissions;

	/** @var array Additional permissions to fetch on object creation, see getPermissions() */
	protected $additionalPermissions = [];

	/**
	 * Overrides the constructor to apply Joomla! ACL permissions
	 *
	 * @param   Container  $container  The container we belong to
	 * @param   array      $config     The configuration overrides for the view
	 */
	public function __construct(Container $container, array $config = [])
	{
		parent::__construct($container, $config);

		$this->permissions = $this->getPermissions(null, $this->additionalPermissions);
	}

	/**
	 * Determines if the current Joomla! version and your current table support AJAX-powered drag and drop reordering.
	 * If they do, it will set up the drag & drop reordering feature.
	 *
	 * @return  null|array  Null if not supported, otherwise a table with necessary information (saveOrder: should
	 *                           you enable DnD reordering; orderingColumn: which column has the ordering information).
	 */
	public function hasAjaxOrderingSupport(): ?array
	{
		/** @var DataModel $model */
		$model = $this->getModel();

		if (!$model->hasField('ordering'))
		{
			return null;
		}

		$listOrder       = $this->escape($model->getState('filter_order', null, 'cmd'));
		$listDir         = $this->escape($model->getState('filter_order_Dir', null, 'cmd'));
		$saveOrder       = $listOrder == $model->getFieldAlias('ordering');
		$saveOrderingUrl = '';

		if ($saveOrder)
		{
			$saveOrderingUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->getName() . '&task=saveorder&format=json';
			$helper          = version_compare(JVERSION, '3.999.999', 'le') ? 'sortablelist.sortable' : 'draggablelist.draggable';

			HtmlHelper::_($helper, 'itemsList', 'adminForm', strtolower($listDir), $saveOrderingUrl);
		}

		return [
			'saveOrder'      => $saveOrder,
			'saveOrderURL'   => $saveOrderingUrl . '&' . $this->container->platform->getToken() . '=1',
			'orderingColumn' => $model->getFieldAlias('ordering'),
		];
	}

	/**
	 * Returns the internal list of useful variables to the benefit of header fields.
	 *
	 * @return \stdClass
	 */
	public function getLists()
	{
		return $this->lists;
	}

	/**
	 * Returns a reference to the permissions object of this view
	 *
	 * @return \stdClass
	 */
	public function getPerms()
	{
		return $this->permissions;
	}

	/**
	 * Returns a reference to the pagination object of this view
	 *
	 * @return Pagination
	 */
	public function getPagination()
	{
		return $this->pagination;
	}

	/**
	 * Get the items collection for browse views
	 *
	 * @return Collection
	 */
	public function getItems()
	{
		return $this->items;
	}

	/**
	 * Get the item for read, edit, add views
	 *
	 * @return DataModel
	 */
	public function getItem()
	{
		return $this->item;
	}

	/**
	 * Get the items count for browse views
	 *
	 * @return int
	 */
	public function getItemCount()
	{
		return $this->itemCount;
	}

	/**
	 * Get the Joomla! page parameters
	 *
	 * @return Registry
	 */
	public function getPageParams()
	{
		return $this->pageParams;
	}

	/**
	 * Returns a permissions object.
	 *
	 * The additionalPermissions array is a hashed array of local key => Joomla! ACL key value pairs. Local key is the
	 * name of the permission in the permissions object, whereas Joomla! ACL key is the name of the ACL permission
	 * known to Joomla! e.g. "core.manage", "foobar.something" and so on.
	 *
	 * Note: on CLI applications all permissions are set to TRUE. There is no ACL check there.
	 *
	 * @param   null|string  $component              The name of the component. Leave empty for automatic detection.
	 * @param   array        $additionalPermissions  Any additional permissions you want to add to the object.
	 *
	 * @return  object
	 */
	protected function getPermissions($component = null, array $additionalPermissions = [])
	{
		// Make sure we have a component
		if (empty($component))
		{
			$component = $this->container->componentName;
		}

		// Initialise with all true
		$permissions = [
			'create'    => true,
			'edit'      => true,
			'editown'   => true,
			'editstate' => true,
			'delete'    => true,
		];

		foreach (array_keys($additionalPermissions) as $localKey)
		{
			$permissions[$localKey] = true;
		}

		$platform = $this->container->platform;

		// If this is a CLI application we don't make any ACL checks
		if ($platform->isCli())
		{
			return (object) $permissions;
		}

		// Get the core permissions
		$permissions = [
			'create'    => $platform->authorise('core.create', $component),
			'edit'      => $platform->authorise('core.edit', $component),
			'editown'   => $platform->authorise('core.edit.own', $component),
			'editstate' => $platform->authorise('core.edit.state', $component),
			'delete'    => $platform->authorise('core.delete', $component),
		];

		foreach ($additionalPermissions as $localKey => $joomlaPermission)
		{
			$permissions[$localKey] = $platform->authorise($joomlaPermission, $component);
		}

		return (object) $permissions;
	}

	/**
	 * Executes before rendering the page for the Browse task.
	 */
	protected function onBeforeBrowse()
	{
		// Create the lists object
		$this->lists = new \stdClass();

		// Load the model
		/** @var DataModel $model */
		$model = $this->getModel();

		// We want to persist the state in the session
		$model->savestate(1);

		// Display limits
		$defaultLimit = 20;

		if (!$this->container->platform->isCli() && class_exists('\Joomla\CMS\Factory'))
		{
			$app = JoomlaFactory::getApplication();

			$defaultLimit = method_exists($app, 'get') ? $app->get('list_limit') : 20;
		}

		$this->lists->limitStart = $model->getState('limitstart', 0, 'int');
		$this->lists->limit      = $model->getState('limit', $defaultLimit, 'int');

		$model->limitstart = $this->lists->limitStart;
		$model->limit      = $this->lists->limit;

		// Assign items to the view
		$this->items     = $model->get(false);
		$this->itemCount = $model->count();

		// Ordering information
		$this->lists->order     = $model->getState('filter_order', $model->getIdFieldName(), 'cmd');
		$this->lists->order_Dir = $model->getState('filter_order_Dir', null, 'cmd');

		if ($this->lists->order_Dir)
		{
			$this->lists->order_Dir = strtolower($this->lists->order_Dir);
		}

		// Pagination
		$this->pagination = new Pagination($this->itemCount, $this->lists->limitStart, $this->lists->limit);

		// Pass page params on frontend only
		if ($this->container->platform->isFrontend())
		{
			/** @var SiteApplication $app */
			$app              = JoomlaFactory::getApplication();
			$params           = $app->getParams();
			$this->pageParams = $params;
		}
	}

	/**
	 * Executes before rendering the page for the add task.
	 */
	protected function onBeforeAdd()
	{
		/** @var DataModel $model */
		$model = $this->getModel();

		/**
		 * The model is pushed into the View by the Controller. As you can see in DataController::add() it is possible
		 * to push both default values (defaultsForAdd) as well as data from the state (e.g. when saving a new record
		 * failed for some reason and the user needs to edit it). That's why we populate defaultFields from $model. We
		 * still do a full reset on a clone of the Model to get a clean object and merge default values (instead of null
		 * values) with the data pushed by the controller.
		 */
		$defaultFields = $model->getData();
		$this->item    = $model->getClone()->reset(true, true);

		foreach ($defaultFields as $k => $v)
		{
			try
			{
				$this->item->setFieldValue($k, $v);
			}
			catch (\Exception $e)
			{
				// Suppress errors in field assignments at this stage
			}
		}
	}

	/**
	 * Executes before rendering the page for the Edit task.
	 */
	protected function onBeforeEdit()
	{
		/** @var DataModel $model */
		$model = $this->getModel();

		// It seems that I can't edit records, maybe I can edit only this one due asset tracking?
		if ((!$this->permissions->edit || !$this->permissions->editown) && is_object($model) && ($model instanceof DataModel))
		{
			// Make sure the model is really asset tracked
			$assetName       = $model->getAssetName();
			$assetName       = is_string($assetName) ? $assetName : null;
			$isAssetsTracked = $model->isAssetsTracked() && !empty($assetName);

			// Ok, record is tracked, let's see if I can this record
			if ($isAssetsTracked)
			{
				$platform = $this->container->platform;


				if (!$this->permissions->edit && !is_null($assetName))
				{
					$this->permissions->edit = $platform->authorise('core.edit', $assetName);
				}

				if (!$this->permissions->editown && !is_null($assetName))
				{
					$this->permissions->editown = $platform->authorise('core.edit.own', $assetName);
				}
			}
		}

		$this->item = $model->findOrFail();
	}

	/**
	 * Executes before rendering the page for the Read task.
	 */
	protected function onBeforeRead()
	{
		/** @var DataModel $model */
		$model = $this->getModel();

		$this->item = $model->findOrFail();
	}
} 
DataView/Json.php000060400000013553152455612700007704 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\DataView;

defined('_JEXEC') || die;

use FOF40\Model\DataModel;
use Joomla\CMS\Document\Document as JoomlaDocument;
use Joomla\CMS\Document\JsonDocument;
use Joomla\CMS\Uri\Uri;

class Json extends Raw implements DataViewInterface
{
	/**
	 * Set to true if your onBefore* methods have already populated the item, items, limitstart etc properties used to
	 * render a JSON document.
	 *
	 * @var bool
	 */
	public $alreadyLoaded = false;

	/**
	 * Record listing offset (how many records to skip before starting showing some)
	 *
	 * @var   int
	 */
	protected $limitStart = 0;

	/**
	 * Record listing limit (how many records to show)
	 *
	 * @var   int
	 */
	protected $limit = 10;

	/**
	 * Total number of records in the result set
	 *
	 * @var   int
	 */
	protected $total = 0;

	/**
	 * The record being displayed
	 *
	 * @var   DataModel
	 */
	protected $item;

	/**
	 * Overrides the default method to execute and display a template script.
	 * Instead of loadTemplate is uses loadAnyTemplate.
	 *
	 * @param   string  $tpl  The name of the template file to parse
	 *
	 * @return  boolean  True on success
	 *
	 * @throws  \Exception  When the layout file is not found
	 */
	public function display($tpl = null)
	{
		$eventName = 'onBefore' . ucfirst($this->doTask);
		$this->triggerEvent($eventName, [$tpl]);

		$eventName = 'onAfter' . ucfirst($this->doTask);
		$this->triggerEvent($eventName, [$tpl]);

		return true;
	}

	/**
	 * The event which runs when we are displaying the record list JSON view
	 *
	 * @param   string  $tpl  The sub-template to use
	 */
	public function onBeforeBrowse($tpl = null)
	{
		// Load the model
		/** @var DataModel $model */
		$model = $this->getModel();

		$result = '';

		if (!$this->alreadyLoaded)
		{
			$this->limitStart = $model->getState('limitstart', 0);
			$this->limit      = $model->getState('limit', 0);
			$this->items      = $model->get(true, $this->limitStart, $this->limit);
			$this->total      = $model->count();
		}

		$document = $this->container->platform->getDocument();

		/** @var JsonDocument $document */
		if ($document instanceof JoomlaDocument)
		{
			$document->setMimeEncoding('application/json');
		}

		if (is_null($tpl))
		{
			$tpl = 'json';
		}

		$hasFailed = false;

		try
		{
			$result = $this->loadTemplate($tpl, true);

			if ($result instanceof \Exception)
			{
				$hasFailed = true;
			}
		}
		catch (\Exception $e)
		{
			$hasFailed = true;
		}

		if ($hasFailed)
		{
			// Default JSON behaviour in case the template isn't there!
			$result = [];

			foreach ($this->items as $item)
			{
				$result[] = (is_object($item) && method_exists($item, 'toArray')) ? $item->toArray() : $item;
			}

			$json = json_encode($result, JSON_PRETTY_PRINT);

			// JSONP support
			$callback = $this->input->get('callback', null, 'raw');

			if (!empty($callback))
			{
				echo $callback . '(' . $json . ')';
			}
			else
			{
				$defaultName = $this->input->get('view', 'main', 'cmd');
				$filename    = $this->input->get('basename', $defaultName, 'cmd');

				$document->setName($filename);
				echo $json;
			}
		}
		else
		{
			echo $result;
		}
	}

	/**
	 * The event which runs when we are displaying a single item JSON view
	 *
	 * @param   string  $tpl  The view sub-template to use
	 */
	protected function onBeforeRead($tpl = null)
	{
		self::renderSingleItem($tpl);
	}

	/**
	 * The event which runs when we are displaying a single item JSON view
	 *
	 * @param   string  $tpl  The view sub-template to use
	 */
	protected function onAfterSave($tpl = null)
	{
		self::renderSingleItem($tpl);
	}

	/**
	 * Renders a single item JSON view
	 *
	 * @param   string  $tpl  The view sub-template to use
	 */
	protected function renderSingleItem($tpl)
	{
		// Load the model
		/** @var DataModel $model */
		$model = $this->getModel();

		$result = '';

		if (!$this->alreadyLoaded)
		{
			$this->item = $model->find();
		}


		$document = $this->container->platform->getDocument();

		/** @var JsonDocument $document */
		if ($document instanceof JoomlaDocument)
		{
			$document->setMimeEncoding('application/json');
		}

		if (is_null($tpl))
		{
			$tpl = 'json';
		}

		$hasFailed = false;

		try
		{
			$result = $this->loadTemplate($tpl, true);

			if ($result instanceof \Exception)
			{
				$hasFailed = true;
			}
		}
		catch (\Exception $e)
		{
			$hasFailed = true;
		}

		if ($hasFailed)
		{
			$data = (is_object($this->item) && method_exists($this->item, 'toArray')) ? $this->item->toArray() : $this->item;

			$json = json_encode($data, JSON_PRETTY_PRINT);

			// JSONP support
			$callback = $this->input->get('callback');

			if (!empty($callback))
			{
				echo $callback . '(' . $json . ')';
			}
			else
			{
				$defaultName = $this->input->get('view', 'main', 'cmd');
				$filename    = $this->input->get('basename', $defaultName, 'cmd');
				$document->setName($filename);

				echo $json;
			}
		}
		else
		{
			echo $result;
		}
	}

	/**
	 * Convert an absolute URI to a relative one
	 *
	 * @param   string  $uri  The URI to convert
	 *
	 * @return  string  The relative URL
	 */
	protected function _removeURIBase($uri)
	{
		static $root = null, $rootlen = 0;

		if (is_null($root))
		{
			$root    = rtrim(Uri::base(false), '/');
			$rootlen = strlen($root);
		}

		if (substr($uri, 0, $rootlen) == $root)
		{
			$uri = substr($uri, $rootlen);
		}

		return ltrim($uri, '/');
	}

	/**
	 * Returns a Uri instance with a prototype URI used as the base for the
	 * other URIs created by the JSON renderer
	 *
	 * @return  Uri  The prototype Uri instance
	 */
	protected function _getPrototypeURIForPagination()
	{
		$protoUri = new Uri('index.php');
		$protoUri->setQuery($this->input->getData());
		$protoUri->delVar('savestate');
		$protoUri->delVar('base_path');

		return $protoUri;
	}
}
DataView/DataViewInterface.php000060400000011001152455612700012302 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace  FOF40\View\DataView;

defined('_JEXEC') || die;

use FOF40\Container\Container;
use Joomla\CMS\Pagination\Pagination;

interface DataViewInterface
{
	/**
	 * Determines if the current Joomla! version and your current table support AJAX-powered drag and drop reordering.
	 * If they do, it will set up the drag & drop reordering feature.
	 *
	 * @return  boolean|array  False if not supported, otherwise a table with necessary information (saveOrder: should
	 *                           you enable DnD reordering; orderingColumn: which column has the ordering information).
	 */
	public function hasAjaxOrderingSupport();

	/**
	 * Returns the internal list of useful variables to the benefit of header fields.
	 *
	 * @return \stdClass
	 */
	public function getLists();

	/**
	 * Returns a reference to the permissions object of this view
	 *
	 * @return \stdClass
	 */
	public function getPerms();

	/**
	 * Returns a reference to the pagination object of this view
	 *
	 * @return Pagination
	 */
	public function getPagination();

	/**
	 * Method to get the view name
	 *
	 * The model name by default parsed using the classname, or it can be set
	 * by passing a $config['name'] in the class constructor
	 *
	 * @return  string  The name of the model
	 *
	 * @throws  \Exception
	 */
	public function getName();

	/**
	 * Returns a reference to the container attached to this View
	 *
	 * @return  Container
	 */
	public function &getContainer();

	/**
	 * Escapes a value for output in a view script.
	 *
	 * @param   mixed $var The output to escape.
	 *
	 * @return  string  The escaped value.
	 */
	public function escape($var);

	/**
	 * Returns the task being rendered by the view
	 *
	 * @return  string
	 */
	public function getTask();

	/**
	 * Get the layout.
	 *
	 * @return  string  The layout name
	 */
	public function getLayout();

	/**
	 * Add a JS script file to the page generated by the CMS.
	 *
	 * There are three combinations of defer and async (see http://www.w3schools.com/tags/att_script_defer.asp):
	 * * $defer false, $async true: The script is executed asynchronously with the rest of the page
	 *   (the script will be executed while the page continues the parsing)
	 * * $defer true, $async false: The script is executed when the page has finished parsing.
	 * * $defer false, $async false. (default) The script is loaded and executed immediately. When it finishes
	 *   loading the browser continues parsing the rest of the page.
	 *
	 * When you are using $defer = true there is no guarantee about the load order of the scripts. Whichever
	 * script loads first will be executed first. The order they appear on the page is completely irrelevant.
	 *
	 * @param   string  $uri     A path definition understood by parsePath, e.g. media://com_example/js/foo.js
	 * @param   string  $version (optional) Version string to be added to the URL
	 * @param   string  $type    MIME type of the script
	 * @param   boolean $defer   Adds the defer attribute, see above
	 * @param   boolean $async   Adds the async attribute, see above
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addJavascriptFile($uri, $version = null, $type = 'text/javascript', $defer = false, $async = false);

	/**
	 * Adds an inline JavaScript script to the page header
	 *
	 * @param   string  $script  The script content to add
	 * @param   string  $type    The MIME type of the script
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addJavascriptInline($script, $type = 'text/javascript');

	/**
	 * Add a CSS file to the page generated by the CMS
	 *
	 * @param   string  $uri      A path definition understood by parsePath, e.g. media://com_example/css/foo.css
	 * @param   string  $version  (optional) Version string to be added to the URL
	 * @param   string  $type     MIME type of the stylesheeet
	 * @param   string  $media    Media target definition of the style sheet, e.g. "screen"
	 * @param   array   $attribs  Array of attributes
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addCssFile($uri, $version = null, $type = 'text/css', $media = null, $attribs = array());

	/**
	 * Adds an inline stylesheet (inline CSS) to the page header
	 *
	 * @param   string  $css   The stylesheet content to add
	 * @param   string  $type  The MIME type of the script
	 *
	 * @return  $this  Self, for chaining
	 */
	public function addCssInline($css, $type = 'text/css');
}
DataView/Html.php000060400000010662152455612700007675 0ustar00<?php
/**
 * @package   FOF
 * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace FOF40\View\DataView;

defined('_JEXEC') || die;

use FOF40\Render\RenderInterface;
use Joomla\CMS\Application\SiteApplication;
use Joomla\CMS\Factory as JoomlaFactory;
use Joomla\CMS\Language\Text;

class Html extends Raw implements DataViewInterface
{
	/** @var bool Should I set the page title in the front-end of the site? */
	public $setFrontendPageTitle = false;

	/** @var string The translation key for the default page title */
	public $defaultPageTitle;

	/**
	 * Should FEFHelpBrowse::orderheader() render the pagination (items per page) dropdown?
	 *
	 * @var   bool
	 * @since 4.0.0
	 */
	public $showBrowsePagination = true;

	/**
	 * Should FEFHelpBrowse::orderheader() render the ordering direction dropdown?
	 *
	 * @var   bool
	 * @since 4.0.0
	 */
	public $showBrowseOrdering = true;

	/**
	 * Should FEFHelpBrowse::orderheader() render the order by item dropdown?
	 *
	 * @var   bool
	 * @since 4.0.0
	 */
	public $showBrowseOrderBy = true;

	public function setPageTitle()
	{
		if (!$this->container->platform->isFrontend())
		{
			return '';
		}

		/** @var SiteApplication $app */
		$app      = JoomlaFactory::getApplication();
		$document = JoomlaFactory::getDocument();
		$menus    = $app->getMenu();
		$menu     = $menus->getActive();

		// Get the option and view name
		$option = $this->container->componentName;
		$view   = $this->getName();

		// Get the default page title translation key
		$default = empty($this->defaultPageTitle) ? $option . '_TITLE_' . $view : $this->defaultPageTitle;

		$params = $app->getParams($option);

		// Set the default value for page_heading
		$params->def('page_heading', ($menu !== null) ? $params->get('page_title', $menu->title) : Text::_($default));

		// Set the document title
		$title    = $params->get('page_title', '');
		$sitename = $app->get('sitename');

		if ($title == $sitename)
		{
			$title = Text::_($default);
		}

		if (empty($title))
		{
			$title = $sitename;
		}
		elseif ($app->get('sitename_pagetitles', 0) == 1)
		{
			$title = Text::sprintf('JPAGETITLE', $app->get('sitename'), $title);
		}
		elseif ($app->get('sitename_pagetitles', 0) == 2)
		{
			$title = Text::sprintf('JPAGETITLE', $title, $app->get('sitename'));
		}

		$document->setTitle($title);

		// Set meta
		if ($params->get('menu-meta_description'))
		{
			$document->setDescription($params->get('menu-meta_description'));
		}

		if ($params->get('menu-meta_keywords'))
		{
			$document->setMetadata('keywords', $params->get('menu-meta_keywords'));
		}

		if ($params->get('robots'))
		{
			$document->setMetadata('robots', $params->get('robots'));
		}

		return $title;
	}

	protected function initialise(): void
	{
		$view = $this->getName();
		$task = $this->task;

		$renderer = $this->container->renderer;
		$renderer->initialise($view, $task);
	}

	/**
	 * Runs before rendering the view template, echoing HTML to put before the
	 * view template's generated HTML
	 *
	 * @return  void
	 *
	 * @throws \Exception
	 */
	protected function preRender(): void
	{
		$view = $this->getName();
		$task = $this->task;

		// Don't load the toolbar on CLI
		$platform = $this->container->platform;

		if (!$platform->isCli())
		{
			$toolbar        = $this->container->toolbar;
			$toolbar->perms = $this->permissions;
			$toolbar->renderToolbar($view, $task);
		}

		if ($platform->isFrontend() && $this->setFrontendPageTitle)
		{
			$this->setPageTitle();
		}

		$renderer = $this->container->renderer;
		$renderer->preRender($view, $task);
	}

	/**
	 * Runs after rendering the view template, echoing HTML to put after the
	 * view template's generated HTML
	 *
	 * @return  void
	 *
	 * @throws \Exception
	 */
	protected function postRender(): void
	{
		$view = $this->getName();
		$task = $this->task;

		$renderer = $this->container->renderer;

		if ($renderer instanceof RenderInterface)
		{
			$renderer->postRender($view, $task);
		}
	}

	/**
	 * Executes before rendering the page for the Add task.
	 */
	protected function onBeforeAdd()
	{
		// Hide main menu
		JoomlaFactory::getApplication()->input->set('hidemainmenu', true);

		parent::onBeforeAdd();
	}

	/**
	 * Executes before rendering the page for the Edit task.
	 */
	protected function onBeforeEdit()
	{
		// Hide main menu
		JoomlaFactory::getApplication()->input->set('hidemainmenu', true);

		parent::onBeforeEdit();
	}
} 
Oauth2/Raw.php000060400000001222152456020260007143 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */
namespace Akeeba\Backup\Site\View\Oauth2;

defined('_JEXEC') || die;

use Akeeba\Backup\Site\Model\Oauth2\ProviderInterface;
use Akeeba\Backup\Site\Model\Oauth2\TokenResponse;
use FOF40\View\DataView\Raw as BaseView;

class Raw extends BaseView
{
	/** @var ProviderInterface|null  */
	public $provider = null;

	/** @var TokenResponse|null  */
	public $tokens = null;

	/** @var \Exception|null  */
	public $exception = null;

	/** @var string|null  */
	public $step1url = null;
}