| Current Path : /home/w/u/e/wuectly/www/03cbe/ |
| Current File : /home/w/u/e/wuectly/www/03cbe/View.tar |
Engine/BladeEngine.php 0000604 00000001052 15245561270 0010620 0 ustar 00 <?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.php 0000604 00000015262 15245561270 0011542 0 ustar 00 <?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.php 0000604 00000001650 15245561270 0010344 0 ustar 00 <?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.php 0000604 00000001572 15245561270 0011520 0 ustar 00 <?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.php 0000604 00000002006 15245561270 0011354 0 ustar 00 <?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.php 0000604 00000001200 15245561270 0011273 0 ustar 00 <?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.php 0000604 00000001220 15245561270 0011726 0 ustar 00 <?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.php 0000604 00000001256 15245561270 0013544 0 ustar 00 <?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.php 0000604 00000001214 15245561270 0011677 0 ustar 00 <?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.php 0000604 00000001307 15245561270 0012374 0 ustar 00 <?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.php 0000604 00000001320 15245561270 0012230 0 ustar 00 <?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.php 0000604 00000001653 15245561270 0012432 0 ustar 00 <?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.php 0000604 00000064375 15245561270 0010060 0 ustar 00 <?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.php 0000604 00000041243 15245561270 0011022 0 ustar 00 <?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.php 0000604 00000102240 15245561270 0006171 0 ustar 00 <?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.php 0000604 00000013361 15245561270 0007523 0 ustar 00 <?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.php 0000604 00000023647 15245561270 0007531 0 ustar 00 <?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.php 0000604 00000013553 15245561270 0007704 0 ustar 00 <?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.php 0000604 00000011001 15245561270 0012302 0 ustar 00 <?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.php 0000604 00000010662 15245561270 0007675 0 ustar 00 <?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.php 0000604 00000001222 15245602026 0007143 0 ustar 00 <?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;
}