| Current Path : /home/digilove/www/41423/ |
| Current File : /home/digilove/www/41423/View.tar |
Compiler/Blade.php 0000644 00000061331 15234642034 0010045 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Compiler;
use FOF30\Container\Container;
use FOF30\Utils\Phpfunc;
defined('_JEXEC') or die;
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 = array();
/**
* 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 = array(
'Extensions',
'Statements',
'Comments',
'Echos'
);
/**
* Array of opening and closing tags for escaped echos.
*
* @var array
*/
protected $contentTags = array('{{', '}}');
/**
* Array of opening and closing tags for escaped echos.
*
* @var array
*/
protected $escapedTags = array('{{{', '}}}');
/**
* Array of footer lines to be added to template.
*
* @var array
*/
protected $footer = array();
/**
* 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') && defined('T_INLINE_HTML'))
{
$this->usingTokenizer = true;
}
}
/**
* Report if the PHP Tokenizer extension is being used
*
* @return bool
*/
public function isUsingTokenizer()
{
return $this->usingTokenizer;
}
/**
* Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return
* false.
*
* @return mixed
*/
public function isCacheable()
{
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 mixed
*/
public function compile($path, array $forceParams = array())
{
$this->footer = array();
$fileData = @file_get_contents($path);
if ($path)
{
$this->setPath($path);
}
return $this->compileString($fileData);
}
/**
* Get the path currently being compiled.
*
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Set the path currently being compiled.
*
* @param string $path
* @return void
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* Compile the given Blade template contents.
*
* @param string $value
* @return string
*/
public function compileString($value)
{
$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;
}
/**
* Parse the tokens from the template.
*
* @param array $token
* @return string
*/
protected function parseToken($token)
{
list($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($value)
{
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($value)
{
$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($value)
{
$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($value)
{
return preg_replace_callback('/\B@(\w+)([ \t]*)(\( ( (?>[^()]+) | (?3) )* \))?/x', array($this, 'compileStatementsCallback'), $value);
}
/**
* Callback for compileStatements, since $this is not allowed in Closures under PHP 5.3.
*
* @param $match
*
* @return string
*/
protected function compileStatementsCallback($match)
{
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($value)
{
$pattern = sprintf('/(@)?%s\s*(.+?)\s*%s(\r?\n)?/s', $this->contentTags[0], $this->contentTags[1]);
return preg_replace_callback($pattern, array($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($matches)
{
$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($value)
{
$pattern = sprintf('/%s\s*(.+?)\s*%s(\r?\n)?/s', $this->escapedTags[0], $this->escapedTags[1]);
return preg_replace_callback($pattern, array($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($matches)
{
$whitespace = empty($matches[2]) ? '' : $matches[2].$matches[2];
return '<?php echo $this->escape('.$this->compileEchoDefaults($matches[1]).'); ?>'.$whitespace;
}
/**
* Compile the default values for the echo statement.
*
* @param string $value
* @return string
*/
public function compileEchoDefaults($value)
{
return preg_replace('/^(?=\$)(.+?)(?:\s+or\s+)(.+?)$/s', 'isset($1) ? $1 : $2', $value);
}
/**
* Compile the each statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEach($expression)
{
return "<?php echo \$this->renderEach{$expression}; ?>";
}
/**
* Compile the yield statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileYield($expression)
{
return "<?php echo \$this->yieldContent{$expression}; ?>";
}
/**
* Compile the show statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileShow($expression)
{
return "<?php echo \$this->yieldSection(); ?>";
}
/**
* Compile the section statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSection($expression)
{
return "<?php \$this->startSection{$expression}; ?>";
}
/**
* Compile the append statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileAppend($expression)
{
return "<?php \$this->appendSection(); ?>";
}
/**
* Compile the end-section statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndsection($expression)
{
return "<?php \$this->stopSection(); ?>";
}
/**
* Compile the stop statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileStop($expression)
{
return "<?php \$this->stopSection(); ?>";
}
/**
* Compile the overwrite statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileOverwrite($expression)
{
return "<?php \$this->stopSection(true); ?>";
}
/**
* Compile the unless statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileUnless($expression)
{
return "<?php if ( ! $expression): ?>";
}
/**
* Compile the end unless statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndunless($expression)
{
return "<?php endif; ?>";
}
/**
* Compile the end repeatable statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileRepeatable($expression)
{
$expression = trim($expression, '()');
$parts = explode(',', $expression, 2);
$functionName = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0]));
$argumentsList = isset($parts[1]) ? $parts[1] : '';
return "<?php @\$$functionName = function($argumentsList) { ?>";
}
/**
* Compile the end endRepeatable statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndRepeatable($expression)
{
return "<?php }; ?>";
}
/**
* Compile the end yieldRepeatable statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileYieldRepeatable($expression)
{
$expression = trim($expression, '()');
$parts = explode(',', $expression, 2);
$functionName = '_fof_blade_repeatable_' . md5($this->path . trim($parts[0]));
$argumentsList = isset($parts[1]) ? $parts[1] : '';
return "<?php \$$functionName($argumentsList); ?>";
}
/**
* Compile the lang statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileLang($expression)
{
return "<?php echo \\JText::_$expression; ?>";
}
/**
* Compile the sprintf statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileSprintf($expression)
{
return "<?php echo \\JText::sprintf$expression; ?>";
}
/**
* Compile the plural statements into valid PHP.
*
* e.g. @plural('COM_FOOBAR_N_ITEMS_SAVED', $countItemsSaved)
*
* @see JText::plural()
*
* @param string $expression
* @return string
*/
protected function compilePlural($expression)
{
return "<?php echo \\JText::plural$expression; ?>";
}
/**
* Compile the token statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileToken($expression)
{
return "<?php echo \$this->container->platform->getToken(true); ?>";
}
/**
* Compile the else statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElse($expression)
{
return "<?php else: ?>";
}
/**
* Compile the for statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileFor($expression)
{
return "<?php for{$expression}: ?>";
}
/**
* Compile the foreach statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileForeach($expression)
{
return "<?php foreach{$expression}: ?>";
}
/**
* Compile the forelse statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileForelse($expression)
{
$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($expression)
{
return "<?php if{$expression}: ?>";
}
/**
* Compile the else-if statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileElseif($expression)
{
return "<?php elseif{$expression}: ?>";
}
/**
* Compile the forelse statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEmpty($expression)
{
$empty = '$__empty_' . $this->forelseCounter--;
return "<?php endforeach; if ({$empty}): ?>";
}
/**
* Compile the while statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileWhile($expression)
{
return "<?php while{$expression}: ?>";
}
/**
* Compile the end-while statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndwhile($expression)
{
return "<?php endwhile; ?>";
}
/**
* Compile the end-for statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndfor($expression)
{
return "<?php endfor; ?>";
}
/**
* Compile the end-for-each statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndforeach($expression)
{
return "<?php endforeach; ?>";
}
/**
* Compile the end-if statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndif($expression)
{
return "<?php endif; ?>";
}
/**
* Compile the end-for-else statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndforelse($expression)
{
return "<?php endif; ?>";
}
/**
* Compile the extends statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileExtends($expression)
{
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($expression)
{
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($expression)
{
if (starts_with($expression, '('))
{
$expression = substr($expression, 1, -1);
}
return "<?php echo \\FOF30\\Layout\\LayoutHelper::render(\$this->container, $expression); ?>";
}
/**
* Compile the stack statements into the content
*
* @param string $expression
* @return string
*/
protected function compileStack($expression)
{
return "<?php echo \$this->yieldContent{$expression}; ?>";
}
/**
* Compile the push statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compilePush($expression)
{
return "<?php \$this->startSection{$expression}; ?>";
}
/**
* Compile the endpush statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEndpush($expression)
{
return "<?php \$this->appendSection(); ?>";
}
/**
* Compile the route statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileRoute($expression)
{
return "<?php echo \$this->container->template->route{$expression}; ?>";
}
/**
* Compile the css statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileCss($expression)
{
return "<?php \$this->addCssFile{$expression}; ?>";
}
/**
* Compile the inlineCss statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileInlineCss($expression)
{
return "<?php \$this->addCssInline{$expression}; ?>";
}
/**
* Compile the inlineJs statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileInlineJs($expression)
{
return "<?php \$this->addJavascriptInline{$expression}; ?>";
}
/**
* Compile the js statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileJs($expression)
{
return "<?php \$this->addJavascriptFile{$expression}; ?>";
}
/**
* Compile the less statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileLess($expression)
{
return "<?php \$this->addLessFile{$expression}; ?>";
}
/**
* Compile the jhtml statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileJhtml($expression)
{
return "<?php echo \\JHtml::_{$expression}; ?>";
}
/**
* Compile the `sortgrid` statements into valid PHP.
*
* @param string $expression
* @return string
*
* @since 3.3.0
*/
protected function compileSortgrid($expression)
{
return "<?php echo FOF30\Utils\FEFHelper\BrowseView::sortGrid{$expression} ?>";
}
/**
* Compile the `fieldtitle` statements into valid PHP.
*
* @param string $expression
* @return string
*
* @since 3.3.0
*/
protected function compileFieldtitle($expression)
{
return "<?php echo FOF30\Utils\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($expression)
{
return "<?php echo \FOF30\Utils\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($expression)
{
return "<?php echo \FOF30\Utils\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($expression)
{
return "<?php echo \FOF30\Utils\FEFHelper\BrowseView::searchFilter{$expression} ?>";
}
/**
* Compile the media statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileMedia($expression)
{
return "<?php echo \$this->container->template->parsePath{$expression}; ?>";
}
/**
* Compile the modules statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileModules($expression)
{
return "<?php echo \$this->container->template->loadPosition{$expression}; ?>";
}
/**
* Compile the module statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileModule($expression)
{
return "<?php echo \$this->container->template->loadModule{$expression}; ?>";
}
/**
* Compile the editor statements into valid PHP.
*
* @param string $expression
* @return string
*/
protected function compileEditor($expression)
{
return '<?php echo JEditor::getInstance($this->container->platform->getConfig()->get(\'editor\', \'tinymce\'))'.
'->display' . $expression . '; ?>';
}
/**
* 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($compiler, $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($tag)
{
return array_key_exists($tag, $this->extensions);
}
/**
* Remove a custom BLade compiler given its optional tag name
*
* @param string $tag
*/
public function removeExtension($tag)
{
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($function)
{
return '/(?<!\w)(\s*)@'.$function.'(\s*\(.*\))/';
}
/**
* Get the regular expression for a generic Blade function.
*
* @param string $function
* @return string
*/
public function createOpenMatcher($function)
{
return '/(?<!\w)(\s*)@'.$function.'(\s*\(.*)\)/';
}
/**
* Create a plain Blade matcher.
*
* @param string $function
* @return string
*/
public function createPlainMatcher($function)
{
return '/(?<!\w)(\s*)@'.$function.'(\s*)/';
}
/**
* Sets the content tags used for the compiler.
*
* @param string $openTag
* @param string $closeTag
* @param bool $escaped
* @return void
*/
public function setContentTags($openTag, $closeTag, $escaped = false)
{
$property = ($escaped === true) ? 'escapedTags' : 'contentTags';
$this->{$property} = array(preg_quote($openTag), preg_quote($closeTag));
}
/**
* Sets the escaped content tags used for the compiler.
*
* @param string $openTag
* @param string $closeTag
* @return void
*/
public function setEscapedContentTags($openTag, $closeTag)
{
$this->setContentTags($openTag, $closeTag, true);
}
/**
* Gets the content tags used for the compiler.
*
* @return string
*/
public function getContentTags()
{
return $this->getTags();
}
/**
* Gets the escaped content tags used for the compiler.
*
* @return string
*/
public function getEscapedContentTags()
{
return $this->getTags(true);
}
/**
* Returns the file extension supported by this compiler
*
* @return string
*
* @since 3.3.1
*/
public function getFileExtension()
{
return $this->fileExtension;
}
/**
* Gets the tags used for the compiler.
*
* @param bool $escaped
* @return array
*/
protected function getTags($escaped = false)
{
$tags = $escaped ? $this->escapedTags : $this->contentTags;
return array_map('stripcslashes', $tags);
}
}
Compiler/CompilerInterface.php 0000644 00000001626 15234642034 0012432 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Compiler;
defined('_JEXEC') or die;
interface CompilerInterface
{
/**
* Are the results of this compiler engine cacheable? If the engine makes use of the forcedParams it must return
* false.
*
* @return mixed
*/
public function 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 mixed
*/
public function compile($path, array $forceParams = array());
/**
* Returns the file extension supported by this compiler
*
* @return string
*
* @since 3.3.1
*/
public function getFileExtension();
}
DataView/Csv.php 0000644 00000013360 15234642034 0007522 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\DataView;
use FOF30\Container\Container;
use FOF30\Model\DataModel;
use FOF30\View\Exception\AccessForbidden;
defined('_JEXEC') or die;
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 = null;
/**
* The columns to include in the CSV output. If it's empty it will be ignored.
*
* @var array
*/
protected $csvFields = array();
/**
* Public constructor. Instantiates a F0FViewCsv object.
*
*
* @param Container $container The container we belong to
* @param array $config The configuration overrides for the view
*/
public function __construct(Container $container, array $config = array())
{
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, array($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 \JDocument)
{
$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 = array();
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 = array();
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 = array();
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, array($tpl));
return true;
}
}
DataView/DataViewInterface.php 0000644 00000013241 15234642034 0012312 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\DataView;
use FOF30\Container\Container;
defined('_JEXEC') or die;
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 \JPagination
*/
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');
/**
* Compile a LESS file into CSS and add it to the page generated by the CMS.
* This method has integrated cache support. The compiled LESS files will be
* written to the media/lib_fof/compiled directory of your site. If the file
* cannot be written we will use the $cssUri, if specified
*
* @param string $uri A path definition understood by parsePath pointing to the source LESS file,
* e.g. media://com_example/less/foo.less
* @param string $cssUri A path definition understood by parsePath pointing to a precompiled CSS file,
* used when we can't write the generated file to the output directory,
* 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 addLess($uri, $cssUri, $version = null, $type = 'text/css', $media = null, $attribs = array());
}
DataView/Html.php 0000644 00000007255 15234642034 0007701 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\DataView;
use FOF30\Render\RenderInterface;
defined('_JEXEC') or die;
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 = null;
/**
* Runs before rendering the view template, echoing HTML to put before the
* view template's generated HTML
*
* @return void
*
* @throws \Exception
*/
protected function preRender()
{
$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()
{
$view = $this->getName();
$task = $this->task;
$renderer = $this->container->renderer;
if ($renderer instanceof RenderInterface)
{
$renderer->postRender($view, $task);
}
}
public function setPageTitle()
{
if (!$this->container->platform->isFrontend())
{
return '';
}
/** @var \JApplicationSite $app */
$app = \JFactory::getApplication();
$document = \JFactory::getDocument();
$menus = $app->getMenu();
$menu = $menus->getActive();
$title = null;
// 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
if ($menu)
{
$params->def('page_heading', $params->get('page_title', $menu->title));
}
else
{
$params->def('page_heading', \JText::_($default));
}
// Set the document title
$title = $params->get('page_title', '');
$sitename = $app->get('sitename');
if ($title == $sitename)
{
$title = \JText::_($default);
}
if (empty($title))
{
$title = $sitename;
}
elseif ($app->get('sitename_pagetitles', 0) == 1)
{
$title = \JText::sprintf('JPAGETITLE', $app->get('sitename'), $title);
}
elseif ($app->get('sitename_pagetitles', 0) == 2)
{
$title = \JText::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;
}
/**
* Executes before rendering the page for the Add task.
*/
protected function onBeforeAdd()
{
// Hide main menu
\JFactory::getApplication()->input->set('hidemainmenu', true);
parent::onBeforeAdd();
}
/**
* Executes before rendering the page for the Edit task.
*/
protected function onBeforeEdit()
{
// Hide main menu
\JFactory::getApplication()->input->set('hidemainmenu', true);
parent::onBeforeEdit();
}
}
DataView/Json.php 0000644 00000024464 15234642034 0007707 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\DataView;
use FOF30\Hal\Document;
use FOF30\Hal\Link;
use FOF30\Model\DataModel;
defined('_JEXEC') or die;
class Json extends Raw implements DataViewInterface
{
/**
* 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 = null;
/**
* When set to true we'll add hypermedia to the output, implementing the
* HAL specification (http://stateless.co/hal_specification.html)
*
* @var boolean
*/
public $useHypermedia = false;
/**
* 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;
/**
* 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, array($tpl));
$eventName = 'onAfter' . ucfirst($this->doTask);
$this->triggerEvent($eventName, array($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 \JDocumentJSON $document */
if ($document instanceof \JDocument)
{
if ($this->useHypermedia)
{
$document->setMimeEncoding('application/hal+json');
}
else
{
$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!
if ($this->useHypermedia)
{
$data = array();
foreach($this->items as $item)
{
if(is_object($item) && method_exists($item, 'toArray'))
{
$data[] = $item->toArray();
}
else
{
$data[] = $item;
}
}
$HalDocument = $this->_createDocumentWithHypermedia($data, $model);
$json = $HalDocument->render('json');
}
else
{
$result = array();
foreach($this->items as $item)
{
if(is_object($item) && method_exists($item, 'toArray'))
{
$result[] = $item->toArray();
}
else
{
$result[] = $item;
}
}
if (version_compare(PHP_VERSION, '5.4', 'ge'))
{
$json = json_encode($result, JSON_PRETTY_PRINT);
}
else
{
$json = json_encode($result);
}
}
// 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 \JDocumentJSON $document */
if ($document instanceof \JDocument)
{
if ($this->useHypermedia)
{
$document->setMimeEncoding('application/hal+json');
}
else
{
$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!
if ($this->useHypermedia)
{
$haldocument = $this->_createDocumentWithHypermedia($this->item, $model);
$json = $haldocument->render('json');
}
else
{
if (is_object($this->item) && method_exists($this->item, 'toArray'))
{
$data = $this->item->toArray();
}
else
{
$data = $this->item;
}
if (version_compare(PHP_VERSION, '5.4', 'ge'))
{
$json = json_encode($data, JSON_PRETTY_PRINT);
}
else
{
$json = json_encode($data);
}
}
// JSONP support
$callback = $this->input->get('callback', null);
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;
}
}
/**
* Creates a \FOF30\Hal\Document using the provided data
*
* @param mixed|array $data The data to put in the document
* @param DataModel $model The model of this view
*
* @return \FOF30\Hal\Document A HAL-enabled document
*/
protected function _createDocumentWithHypermedia($data, $model = null)
{
// Create a new HAL document
if (is_array($data))
{
$count = count($data);
}
else
{
$count = null;
}
if ($count == 1)
{
reset($data);
$document = new Document(end($data));
}
else
{
$document = new Document($data);
}
// Create a self link
$uri = (string) (\JUri::getInstance());
$uri = $this->_removeURIBase($uri);
$uri = \JRoute::_($uri);
$document->addLink('self', new Link($uri));
// Create relative links in a record list context
if (is_array($data) && ($model instanceof DataModel))
{
if(!isset($this->total))
{
$this->total = $model->count();
}
if(!isset($this->limitStart))
{
$this->limitStart = $model->getState('limitstart', 0);
}
if(!isset($this->limit))
{
$this->limit = $model->getState('limit', 0);
}
$pagination = new \JPagination($this->total, $this->limitStart, $this->limit);
if ($pagination->pagesTotal > 1)
{
// Try to guess URL parameters and create a prototype URL
// NOTE: You are better off specialising this method
$protoUri = $this->_getPrototypeURIForPagination();
// The "first" link
$uri = clone $protoUri;
$uri->setVar('limitstart', 0);
$uri = \JRoute::_($uri);
$document->addLink('first', new Link($uri));
// Do we need a "prev" link?
if ($pagination->pagesCurrent > 1)
{
$prevPage = $pagination->pagesCurrent - 1;
$limitstart = ($prevPage - 1) * $pagination->limit;
$uri = clone $protoUri;
$uri->setVar('limitstart', $limitstart);
$uri = \JRoute::_($uri);
$document->addLink('prev', new Link($uri));
}
// Do we need a "next" link?
if ($pagination->pagesCurrent < $pagination->pagesTotal)
{
$nextPage = $pagination->pagesCurrent + 1;
$limitstart = ($nextPage - 1) * $pagination->limit;
$uri = clone $protoUri;
$uri->setVar('limitstart', $limitstart);
$uri = \JRoute::_($uri);
$document->addLink('next', new Link($uri));
}
// The "last" link?
$lastPage = $pagination->pagesTotal;
$limitstart = ($lastPage - 1) * $pagination->limit;
$uri = clone $protoUri;
$uri->setVar('limitstart', $limitstart);
$uri = \JRoute::_($uri);
$document->addLink('last', new Link($uri));
}
}
return $document;
}
/**
* 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(\JUri::base(false), '/');
$rootlen = strlen($root);
}
if (substr($uri, 0, $rootlen) == $root)
{
$uri = substr($uri, $rootlen);
}
return ltrim($uri, '/');
}
/**
* Returns a JUri instance with a prototype URI used as the base for the
* other URIs created by the JSON renderer
*
* @return \JUri The prototype JUri instance
*/
protected function _getPrototypeURIForPagination()
{
$protoUri = new \JUri('index.php');
$protoUri->setQuery($this->input->getData());
$protoUri->delVar('savestate');
$protoUri->delVar('base_path');
return $protoUri;
}
}
DataView/Raw.php 0000644 00000022660 15234642034 0007523 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\DataView;
use FOF30\Container\Container;
use FOF30\Model\DataModel;
use FOF30\Model\DataModel\Collection;
use FOF30\View\View;
use Joomla\Registry\Registry;
defined('_JEXEC') or die;
/**
* View for a raw data-driven view
*/
class Raw extends View implements DataViewInterface
{
/** @var \stdClass Data lists */
protected $lists = null;
/** @var \JPagination The pagination object */
protected $pagination = null;
/** @var \JRegistry|Registry Page parameters object, for front-end views */
protected $pageParams = null;
/** @var Collection The records loaded (browse views) */
protected $items = null;
/** @var DataModel The record loaded (read, edit, add views) */
protected $item = null;
/** @var int The total number of items in the model (more than those loaded) */
protected $itemCount = 0;
/** @var \stdClass ACL permissions map */
protected $permissions = null;
/** @var array Additional permissions to fetch on object creation, see getPermissions() */
protected $additionalPermissions = array();
/**
* 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 = array())
{
parent::__construct($container, $config);
$this->permissions = $this->getPermissions(null, $this->additionalPermissions);
}
/**
* 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 = array())
{
// Make sure we have a component
if (empty($component))
{
$component = $this->container->componentName;
}
// Initialise with all true
$permissions = array(
'create' => true,
'edit' => true,
'editown' => true,
'editstate' => true,
'delete' => true,
);
if (!empty($additionalPermissions))
{
foreach ($additionalPermissions as $localKey => $joomlaPermission)
{
$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 = array(
'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),
);
if (!empty($additionalPermissions))
{
foreach ($additionalPermissions as $localKey => $joomlaPermission)
{
$permissions[$localKey] = $platform->authorise($joomlaPermission, $component);
}
}
return (object)$permissions;
}
/**
* 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()
{
/** @var DataModel $model */
$model = $this->getModel();
if (!$model->hasField('ordering'))
{
return false;
}
$listOrder = $this->escape($model->getState('filter_order', null, 'cmd'));
$listDirn = $this->escape($model->getState('filter_order_Dir', null, 'cmd'));
$saveOrder = $listOrder == $model->getFieldAlias('ordering');
if ($saveOrder)
{
$saveOrderingUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->getName() . '&task=saveorder&format=json';
\JHtml::_('sortablelist.sortable', 'itemsList', 'adminForm', strtolower($listDirn), $saveOrderingUrl);
}
return array(
'saveOrder' => $saveOrder,
'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 \JPagination
*/
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 \JRegistry|Registry
*/
public function getPageParams()
{
return $this->pageParams;
}
/**
* 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('JFactory'))
{
$app = \JFactory::getApplication();
if (method_exists($app, 'get'))
{
$defaultLimit = $app->get('list_limit');
}
else
{
$defaultLimit = 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 \JPagination($this->itemCount, $this->lists->limitStart, $this->lists->limit);
// Pass page params on frontend only
if ($this->container->platform->isFrontend())
{
/** @var \JApplicationSite $app */
$app = \JFactory::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)
{
if ($model)
{
// Ok, record is tracked, let's see if I can this record
if ($model->isAssetsTracked())
{
$platform = $this->container->platform;
if (!$this->permissions->edit)
{
$this->permissions->edit = $platform->authorise('core.edit', $model->getAssetName());
}
if (!$this->permissions->editown)
{
$this->permissions->editown = $platform->authorise('core.edit.own', $model->getAssetName());
}
}
}
}
$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();
}
}
Engine/AbstractEngine.php 0000644 00000001761 15234642034 0011363 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Engine;
use FOF30\View\View;
defined('_JEXEC') or die;
abstract class AbstractEngine implements EngineInterface
{
/** @var View The view we belong to */
protected $view = null;
/**
* 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 = array())
{
return array(
'type' => 'raw',
'content' => ''
);
}
}
Engine/BladeEngine.php 0000644 00000001033 15234642034 0010617 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Engine;
use FOF30\View\View;
defined('_JEXEC') or die;
/**
* 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 0000644 00000016721 15234642034 0011543 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Engine;
use FOF30\Utils\Buffer;
use FOF30\View\Compiler\CompilerInterface;
use FOF30\View\Exception\PossiblySuhosin;
defined('_JEXEC') or die;
/**
* View engine for compiling PHP template files.
*/
abstract class CompilingEngine extends AbstractEngine implements EngineInterface
{
/** @var CompilerInterface The compiler used by this engine */
protected $compiler = null;
/**
* 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 = array())
{
/**
* If the PHP Tokenizer extension is disabled or not present we try to fall back to precompiled templates.
*
* As we found out the hard way, there are still some hosts which disable / don't load the Tokenizer citing
* "security concerns". What they don't understand is that the tokenizer is basically a read-only extension. It
* can only read PHP code and break it into tokens, it cannot execute it. In fact, you can't easily convert
* tokenizer results back into executable PHP. In fact, you'd need https://github.com/nikic/PHP-Parser which is
* by no means trivial. Then again, it STILL cannot execute the PHP without either using 3v4l (sorry for the
* leetspeak, we have to deal with broken hosts killing our file due to this comment!) or writing to a PHP file
* and executing it. In short, the hosts which disable tokenizer don't know how PHP works, don't know the first
* thing about security and must NOT be trusted! If you are on such a host, run away fast, don't look back!
*/
if (!function_exists('token_get_all'))
{
$precompiledPath = $this->getPrecompiledPath($path);
if (($precompiledPath !== false) && @file_exists($precompiledPath))
{
return array(
'type' => 'path',
'content' => $precompiledPath,
);
}
/**
* No precompiled templates and tokenized missing, i.e. I can't compile anything. Instead of throwing a
* fatal error I will throw a catchable runtime error explaining the error condition and how to solve it.
* If your extension does not trap the exception it will bubble up to Joomla's error handler which will
* display this message.
*/
throw new \RuntimeException("Your hosting provider has disabled the <code>token_get_all()</code> PHP function or they have not installed the Tokenizer extension for PHP. This is a safe and <em>secure</em> function of modern PHP, required to convert template files into HTML code your browser can display. Please ask them to enable it. This error occurred trying to render the template file <code>$path</code>", 500);
}
// If it's cached return the path to the cached file's path
if ($this->isCached($path))
{
return array(
'type' => 'path',
'content' => $this->getCachePath($path),
);
}
// Compile it and cache it.
$content = $this->compile($path, $forceParams);
$cachePath = $this->putToCache($path, $content);
// If we could cache it, return the cached file's path
if ($cachePath !== false)
{
return array(
'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);
return array(
'type' => 'path',
'content' => $streamPath,
);
}
// I couldn't use a stream wrapper. I have to give up.
throw new PossiblySuhosin;
}
/**
* 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 = array())
{
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 (!class_exists('JFile'))
{
\JLoader::import('joomla.filesystem.file');
}
if (\JFile::write($cachePath, $content))
{
return $cachePath;
}
return false;
}
/**
* 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')
{
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;
}
}
Engine/EngineInterface.php 0000644 00000001553 15234642034 0011517 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Engine;
use FOF30\View\View;
defined('_JEXEC') or die;
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/PhpEngine.php 0000644 00000001631 15234642034 0010343 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Engine;
defined('_JEXEC') or 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
);
}
}
Exception/AccessForbidden.php 0000644 00000001212 15234642034 0012230 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Exception;
use Exception;
defined('_JEXEC') or die;
/**
* Exception thrown when the access to the requested resource is forbidden under the current execution context
*/
class AccessForbidden extends \RuntimeException
{
public function __construct( $message = "", $code = 403, Exception $previous = null )
{
if (empty($message))
{
$message = \JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN');
}
parent::__construct( $message, $code, $previous );
}
}
Exception/CannotGetName.php 0000644 00000001100 15234642034 0011671 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Exception;
use Exception;
defined('_JEXEC') or die;
/**
* Exception thrown when we can't get a Controller's name
*/
class CannotGetName extends \RuntimeException
{
public function __construct($message = "", $code = 500, Exception $previous = null)
{
if (empty($message))
{
$message = \JText::_('LIB_FOF_VIEW_ERR_GET_NAME');
}
parent::__construct($message, $code, $previous);
}
}
Exception/EmptyStack.php 0000644 00000001064 15234642034 0011303 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Exception;
use Exception;
defined('_JEXEC') or die;
/**
* Exception thrown when we are trying to operate on an empty section stack
*/
class EmptyStack extends \RuntimeException
{
public function __construct($message = "", $code = 500, Exception $previous = null)
{
$message = \JText::_('LIB_FOF_VIEW_EMPTYSECTIONSTACK');
parent::__construct($message, $code, $previous);
}
}
Exception/ModelNotFound.php 0000644 00000001075 15234642034 0011736 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Exception;
use Exception;
defined('_JEXEC') or die;
/**
* Exception thrown when we can't get a Controller's name
*/
class ModelNotFound extends \RuntimeException
{
public function __construct($path, $viewName, $code = 500, Exception $previous = null)
{
$message = \JText::sprintf('LIB_FOF_VIEW_MODELNOTINVIEW', $path, $viewName);
parent::__construct($message, $code, $previous);
}
}
Exception/PossiblySuhosin.php 0000644 00000001177 15234642034 0012401 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Exception;
use Exception;
defined('_JEXEC') or die;
/**
* Exception thrown when the access to the requested resource is forbidden under the current execution context
*/
class PossiblySuhosin extends \RuntimeException
{
public function __construct( $message = "", $code = 403, Exception $previous = null )
{
if (empty($message))
{
$message = \JText::_('LIB_FOF_VIEW_POSSIBLYSUHOSIN');
}
parent::__construct( $message, $code, $previous );
}
}
Exception/UnrecognisedExtension.php 0000644 00000001131 15234642034 0013534 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\Exception;
use Exception;
defined('_JEXEC') or die;
/**
* Exception thrown when we can't figure out which engine to use for a view template
*/
class UnrecognisedExtension extends \InvalidArgumentException
{
public function __construct($path, $code = 500, Exception $previous = null)
{
$message = \JText::sprintf('LIB_FOF_VIEW_UNRECOGNISEDEXTENSION', $path);
parent::__construct($message, $code, $previous);
}
}
View.php 0000644 00000105306 15234642034 0006177 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View;
use FOF30\Container\Container;
use FOF30\Model\Model;
use FOF30\View\Engine\EngineInterface;
use FOF30\View\Exception\AccessForbidden;
use FOF30\View\Exception\CannotGetName;
use FOF30\View\Exception\EmptyStack;
use FOF30\View\Exception\ModelNotFound;
use FOF30\View\Exception\UnrecognisedExtension;
defined('_JEXEC') or die;
/**
* Class View
*
* A generic MVC view implementation
*
* @property-read \FOF30\Input\Input $input The input object (magic __get returns the Input from the Container)
*/
class View
{
public $baseurl = null;
/**
* The name of the view
*
* @var array
*/
protected $name = null;
/**
* Registered models
*
* @var array
*/
protected $modelInstances = array();
/**
* The default model
*
* @var string
*/
protected $defaultModel = null;
/**
* 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 = array();
/**
* The name of the default template source file.
*
* @var string
*/
protected $template = null;
/**
* The output of the template script.
*
* @var string
*/
protected $output = null;
/**
* A cached copy of the configuration
*
* @var array
*/
protected $config = array();
/**
* The container attached to this view
*
* @var Container
*/
protected $container;
/**
* The object used to locate view templates in the filesystem
*
* @var ViewTemplateFinder
*/
protected $viewFinder = null;
/**
* Used when loading template files to avoid variable scope issues
*
* @var null
*/
protected $_tempFilePath = null;
/**
* 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;
/**
* 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 = array(
'.blade.php' => 'FOF30\\View\\Engine\\BladeEngine',
'.php' => 'FOF30\\View\\Engine\\PhpEngine',
);
/**
* All of the finished, captured sections.
*
* @var array
*/
protected $sections = array();
/**
* The stack of in-progress sections.
*
* @var array
*/
protected $sectionStack = array();
/**
* 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 = array();
/**
* 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 = array())
{
$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 (!is_array($config['viewEngineMap']))
{
$temp = explode(',', $config['viewEngineMap']);
$config['viewEngineMap'] = array();
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];
}
}
$this->viewEngineMap = array_merge($this->viewEngineMap, $config['viewEngineMap']);
}
// 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;
}
/**
* 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 = array();
// 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', array(
$this->container->componentName,
$this->getName()
));
if (is_array($extraPathsResults) && !empty($extraPathsResults))
{
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
settype($path, 'array');
// 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);
}
}
/**
* 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)
{
// If $model is null we use the default model
if (is_null($modelName))
{
$model = $this->defaultModel;
}
else
{
$model = $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;
}
else
{
$result = $this->modelInstances[$model]->$property();
if (is_null($result))
{
return $default;
}
return $result;
}
}
// If the model doesn't exist, try to fetch a View property
else
{
if (@isset($this->$property))
{
return $this->$property;
}
else
{
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)
{
$eventName = 'onBefore' . ucfirst($this->doTask);
$this->triggerEvent($eventName, array($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, array($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(array(
'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->raiseError($result->getCode(), $result->getMessage());
}
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 $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
*
* @return string The output of the template
*
* @throws \Exception When the layout file is not found
*/
public function loadAnyTemplate($uri = '', $forceParams = array(), $callback = null, $noOverride = false)
{
if (isset($this->viewTemplateAliases[$uri]))
{
$uri = $this->viewTemplateAliases[$uri];
}
$layoutTemplate = $this->getLayoutTemplate();
$extraPaths = array();
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 subtemplate 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 subtemplate (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 = array('key' => $key, $eachItemName => $value);
$result .= $this->loadAnyTemplate($viewTemplate, $data);
}
}
// If there is no data in the array, we will render the contents of the empty
// view. Alternatively, the "empty view" could be a raw string that begins
// with "raw|" for convenience and to let this know that it is a string. Or
// a language string starting with text|.
else
{
if (starts_with($empty, 'raw|'))
{
$result = substr($empty, 4);
}
elseif (starts_with($empty, 'text|'))
{
$result = \JText::_(substr($empty, 5));
}
else
{
$result = $this->loadAnyTemplate($empty);
}
}
return $result;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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 = array();
$this->sectionStack = array();
}
/**
* Flush all of the section contents if done rendering.
*
* @return void
*/
public function flushSectionsIfDoneRendering()
{
if ($this->doneRendering())
{
$this->flushSections();
}
}
/**
* 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();
}
/**
* 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'];
}
/**
* 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 layout template.
*
* @return string The layout template name
*/
public function getLayoutTemplate()
{
return $this->layoutTemplate;
}
/**
* 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);
});
}
/**
* 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;
}
/**
* 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 = array())
{
$result = true;
// If there is an object method for this event, call it
if (method_exists($this, $event))
{
switch (count($arguments))
{
case 0:
$result = $this->{$event}();
break;
case 1:
$result = $this->{$event}($arguments[0]);
break;
case 2:
$result = $this->{$event}($arguments[0], $arguments[1]);
break;
case 3:
$result = $this->{$event}($arguments[0], $arguments[1], $arguments[2]);
break;
case 4:
$result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
break;
case 5:
$result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]);
break;
default:
$result = call_user_func_array(array($this, $event), $arguments);
break;
}
}
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);
if (!empty($results))
{
foreach ($results as $result)
{
if ($result === false)
{
return false;
}
}
}
return true;
}
/**
* 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;
}
/**
* Runs before rendering the view template, echoing HTML to put before the
* view template's generated HTML
*
* @return void
*/
protected function preRender()
{
// 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()
{
// You need to implement this in children classes
}
/**
* 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;
}
/**
* This method is called by the Renderer when an XML form includes a view template. The $model variable contains the
* Model object instance used by the form. You can override this method to populate your View class with data from
* that model.
*
* @param \FOF30\Model\Model $model The model object passed from the XML form renderer
*
* @deprecated 3.1 Support for XML forms will be removed in FOF 4
*/
public function populateFromModel(Model $model)
{
// Override this method if you need to populate your view from the model passed by an XML form
}
/**
* 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 = array())
{
// 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;
}
/**
* Compile a LESS file into CSS and add it to the page generated by the CMS.
* This method has integrated cache support. The compiled LESS files will be
* written to the media/lib_fof/compiled directory of your site. If the file
* cannot be written we will use the $cssUri, if specified
*
* @param string $uri A path definition understood by parsePath pointing to the source LESS file,
* e.g. media://com_example/less/foo.less
* @param string $cssUri A path definition understood by parsePath pointing to a precompiled CSS file,
* used when we can't write the generated file to the output directory,
* 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 addLess($uri, $cssUri, $version = null, $type = 'text/css', $media = null, $attribs = array())
{
// 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->addLESS($uri, $cssUri, false, $version, $type, $media, $attribs);
return $this;
}
}
ViewTemplateFinder.php 0000644 00000035356 15234642034 0011032 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View;
use FOF30\Container\Container;
defined('_JEXEC') or die;
/**
* 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 subtemplate 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 subtemplate or also no subtemplate? */
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 subtemplate
* 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 subtemplate or also for no subtemplate?
* sidePrefix string The application side prefix (site, admin, auto, any)
*
* @param array $parameters See above
*
* @return array
*/
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
*
* @return string
*
* @throws \RuntimeException
*/
public function resolveUriToPath($uri, $layoutTemplate = '', array $extraPaths = array(), $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 = array();
// 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 this side of the application
$paths[] = ($isAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/ViewTemplates/' . $parts['view'];
$paths[] = ($isAdmin ? $componentPaths['admin'] : $componentPaths['site']) . '/View/' . $parts['view'] . '/tmpl';
// Add the other side of the application for "any:" URIs
if ($parts['admin'] == -1)
{
$paths[] = ($isAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/ViewTemplates/' . $parts['view'];
$paths[] = ($isAdmin ? $componentPaths['site'] : $componentPaths['admin']) . '/View/' . $parts['view'] . '/tmpl';
}
// 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));
}
$filesystem = $this->container->filesystem;
foreach ($this->extensions as $extension)
{
$filenameToFind = $parts['template'] . $extension;
$fileName = $filesystem->pathFind($paths, $filenameToFind);
if ($fileName)
{
return $fileName;
}
}
throw new \RuntimeException(\JText::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $uri), 500);
}
/**
* Get the list of view template extensions
*
* @return array
*/
public function getExtensions()
{
return $this->extensions;
}
/**
* Set the list of view template extensions
*
* @param array $extensions
*
* @return void
*/
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
*/
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 subtemplate name
*
* @return string
*/
public function getDefaultTpl()
{
return $this->defaultTpl;
}
/**
* Sets the default subtemplate name
*
* @param string $defaultTpl
*/
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
*/
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
*/
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
* subtemplate defined in getViewTemplateUris. If it's true we will only look for the subtemplate specified.
*
* @return boolean
*/
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
* subtemplate defined in getViewTemplateUris. If it's true we will only look for the subtemplate specified.
*
* @param boolean $strictTpl
*
* @return void
*/
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
*/
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
*/
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
*/
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;
}
}
HtmlView.php 0000644 00000047475 15235157362 0007046 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\MVC\View;
defined('JPATH_PLATFORM') or die;
/**
* Base class for a Joomla View
*
* Class holding methods for displaying presentation data.
*
* @since 2.5.5
*/
class HtmlView extends \JObject
{
/**
* The active document object
*
* @var \JDocument
* @since 3.0
*/
public $document;
/**
* The name of the view
*
* @var array
* @since 3.0
*/
protected $_name = null;
/**
* Registered models
*
* @var array
* @since 3.0
*/
protected $_models = array();
/**
* The base path of the view
*
* @var string
* @since 3.0
*/
protected $_basePath = null;
/**
* The default model
*
* @var string
* @since 3.0
*/
protected $_defaultModel = null;
/**
* Layout name
*
* @var string
* @since 3.0
*/
protected $_layout = 'default';
/**
* Layout extension
*
* @var string
* @since 3.0
*/
protected $_layoutExt = 'php';
/**
* Layout template
*
* @var string
* @since 3.0
*/
protected $_layoutTemplate = '_';
/**
* The set of search directories for resources (templates)
*
* @var array
* @since 3.0
*/
protected $_path = array('template' => array(), 'helper' => array());
/**
* The name of the default template source file.
*
* @var string
* @since 3.0
*/
protected $_template = null;
/**
* The output of the template script.
*
* @var string
* @since 3.0
*/
protected $_output = null;
/**
* Callback for escaping.
*
* @var string
* @since 3.0
* @deprecated 3.0
*/
protected $_escape = 'htmlspecialchars';
/**
* Charset to use in escaping mechanisms; defaults to urf8 (UTF-8)
*
* @var string
* @since 3.0
*/
protected $_charset = 'UTF-8';
/**
* Constructor
*
* @param array $config A named configuration array for object construction.
* name: the name (optional) of the view (defaults to the view class name suffix).
* charset: the character set to use for display
* escape: the name (optional) of the function to use for escaping strings
* base_path: the parent path (optional) of the views directory (defaults to the component folder)
* template_plath: the path (optional) of the layout directory (defaults to base_path + /views/ + view name
* helper_path: the path (optional) of the helper files (defaults to base_path + /helpers/)
* layout: the layout (optional) to use to display the view
*
* @since 3.0
*/
public function __construct($config = array())
{
// Set the view name
if (empty($this->_name))
{
if (array_key_exists('name', $config))
{
$this->_name = $config['name'];
}
else
{
$this->_name = $this->getName();
}
}
// Set the charset (used by the variable escaping functions)
if (array_key_exists('charset', $config))
{
\JLog::add('Setting a custom charset for escaping is deprecated. Override \JViewLegacy::escape() instead.', \JLog::WARNING, 'deprecated');
$this->_charset = $config['charset'];
}
// User-defined escaping callback
if (array_key_exists('escape', $config))
{
$this->setEscape($config['escape']);
}
// Set a base path for use by the view
if (array_key_exists('base_path', $config))
{
$this->_basePath = $config['base_path'];
}
else
{
$this->_basePath = JPATH_COMPONENT;
}
// Set the default template search path
if (array_key_exists('template_path', $config))
{
// User-defined dirs
$this->_setPath('template', $config['template_path']);
}
elseif (is_dir($this->_basePath . '/view'))
{
$this->_setPath('template', $this->_basePath . '/view/' . $this->getName() . '/tmpl');
}
else
{
$this->_setPath('template', $this->_basePath . '/views/' . $this->getName() . '/tmpl');
}
// Set the default helper search path
if (array_key_exists('helper_path', $config))
{
// User-defined dirs
$this->_setPath('helper', $config['helper_path']);
}
else
{
$this->_setPath('helper', $this->_basePath . '/helpers');
}
// Set the layout
if (array_key_exists('layout', $config))
{
$this->setLayout($config['layout']);
}
else
{
$this->setLayout('default');
}
$this->baseurl = \JUri::base(true);
}
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @see \JViewLegacy::loadTemplate()
* @since 3.0
*/
public function display($tpl = null)
{
$result = $this->loadTemplate($tpl);
if ($result instanceof \Exception)
{
return $result;
}
echo $result;
}
/**
* Assigns variables to the view script via differing strategies.
*
* This method is overloaded; you can assign all the properties of
* an object, an associative array, or a single value by name.
*
* You are not allowed to set variables that begin with an underscore;
* these are either private properties for \JView or private variables
* within the template script itself.
*
* <code>
* $view = new \Joomla\CMS\View\HtmlView;
*
* // Assign directly
* $view->var1 = 'something';
* $view->var2 = 'else';
*
* // Assign by name and value
* $view->assign('var1', 'something');
* $view->assign('var2', 'else');
*
* // Assign by assoc-array
* $ary = array('var1' => 'something', 'var2' => 'else');
* $view->assign($obj);
*
* // Assign by object
* $obj = new \stdClass;
* $obj->var1 = 'something';
* $obj->var2 = 'else';
* $view->assign($obj);
*
* </code>
*
* @return boolean True on success, false on failure.
*
* @since 3.0
* @deprecated 3.0 Use native PHP syntax.
*/
public function assign()
{
\JLog::add(__METHOD__ . ' is deprecated. Use native PHP syntax.', \JLog::WARNING, 'deprecated');
// Get the arguments; there may be 1 or 2.
$arg0 = @func_get_arg(0);
$arg1 = @func_get_arg(1);
// Assign by object
if (is_object($arg0))
{
// Assign public properties
foreach (get_object_vars($arg0) as $key => $val)
{
if (strpos($key, '_') !== 0)
{
$this->$key = $val;
}
}
return true;
}
// Assign by associative array
if (is_array($arg0))
{
foreach ($arg0 as $key => $val)
{
if (strpos($key, '_') !== 0)
{
$this->$key = $val;
}
}
return true;
}
// Assign by string name and mixed value.
// We use array_key_exists() instead of isset() because isset()
// fails if the value is set to null.
if (is_string($arg0) && strpos($arg0, '_') !== 0 && func_num_args() > 1)
{
$this->$arg0 = $arg1;
return true;
}
// $arg0 was not object, array, or string.
return false;
}
/**
* Assign variable for the view (by reference).
*
* You are not allowed to set variables that begin with an underscore;
* these are either private properties for \JView or private variables
* within the template script itself.
*
* <code>
* $view = new \JView;
*
* // Assign by name and value
* $view->assignRef('var1', $ref);
*
* // Assign directly
* $view->var1 = &$ref;
* </code>
*
* @param string $key The name for the reference in the view.
* @param mixed &$val The referenced variable.
*
* @return boolean True on success, false on failure.
*
* @since 3.0
* @deprecated 3.0 Use native PHP syntax.
*/
public function assignRef($key, &$val)
{
\JLog::add(__METHOD__ . ' is deprecated. Use native PHP syntax.', \JLog::WARNING, 'deprecated');
if (is_string($key) && strpos($key, '_') !== 0)
{
$this->$key = &$val;
return true;
}
return false;
}
/**
* Escapes a value for output in a view script.
*
* If escaping mechanism is either htmlspecialchars or htmlentities, uses
* {@link $_encoding} setting.
*
* @param mixed $var The output to escape.
*
* @return mixed The escaped value.
*
* @note the ENT_COMPAT flag will be replaced by ENT_QUOTES in Joomla 4.0 to also escape single quotes
*
* @since 3.0
*/
public function escape($var)
{
if (in_array($this->_escape, array('htmlspecialchars', 'htmlentities')))
{
return call_user_func($this->_escape, $var, ENT_COMPAT, $this->_charset);
}
return call_user_func($this->_escape, $var);
}
/**
* 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 name of the model to reference or the default value [optional]
*
* @return mixed The return value of the method
*
* @since 3.0
*/
public function get($property, $default = null)
{
// If $model is null we use the default model
if ($default === null)
{
$model = $this->_defaultModel;
}
else
{
$model = strtolower($default);
}
// First check to make sure the model requested exists
if (isset($this->_models[$model]))
{
// Model exists, let's build the method name
$method = 'get' . ucfirst($property);
// Does the method exist?
if (method_exists($this->_models[$model], $method))
{
// The method exists, let's call it and return what we get
$result = $this->_models[$model]->$method();
return $result;
}
}
// Degrade to \JObject::get
$result = parent::get($property, $default);
return $result;
}
/**
* Method to get the model object
*
* @param string $name The name of the model (optional)
*
* @return mixed \JModelLegacy object
*
* @since 3.0
*/
public function getModel($name = null)
{
if ($name === null)
{
$name = $this->_defaultModel;
}
return $this->_models[strtolower($name)];
}
/**
* Get the layout.
*
* @return string The layout name
*
* @since 3.0
*/
public function getLayout()
{
return $this->_layout;
}
/**
* Get the layout template.
*
* @return string The layout template name
*
* @since 3.0
*/
public function getLayoutTemplate()
{
return $this->_layoutTemplate;
}
/**
* 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
*
* @since 3.0
* @throws \Exception
*/
public function getName()
{
if (empty($this->_name))
{
$classname = get_class($this);
$viewpos = strpos($classname, 'View');
if ($viewpos === false)
{
throw new \Exception(\JText::_('JLIB_APPLICATION_ERROR_VIEW_GET_NAME'), 500);
}
$this->_name = strtolower(substr($classname, $viewpos + 4));
}
return $this->_name;
}
/**
* Method to add a model to the view. We support a multiple model single
* view system by which models are referenced by classname. A caveat to the
* classname referencing is that any classname prepended by \JModel will be
* referenced by the name without \JModel, eg. \JModelCategory is just
* Category.
*
* @param \JModelLegacy $model The model to add to the view.
* @param boolean $default Is this the default model?
*
* @return \JModelLegacy The added model.
*
* @since 3.0
*/
public function setModel($model, $default = false)
{
$name = strtolower($model->getName());
$this->_models[$name] = $model;
if ($default)
{
$this->_defaultModel = $name;
}
return $model;
}
/**
* Sets the layout name to use
*
* @param string $layout The layout name or a string in format <template>:<layout file>
*
* @return string Previous value.
*
* @since 3.0
*/
public function setLayout($layout)
{
$previous = $this->_layout;
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;
}
/**
* Allows a different extension for the layout files to be used
*
* @param string $value The extension.
*
* @return string Previous value
*
* @since 3.0
*/
public function setLayoutExt($value)
{
$previous = $this->_layoutExt;
if ($value = preg_replace('#[^A-Za-z0-9]#', '', trim($value)))
{
$this->_layoutExt = $value;
}
return $previous;
}
/**
* Sets the _escape() callback.
*
* @param mixed $spec The callback for _escape() to use.
*
* @return void
*
* @since 3.0
* @deprecated 3.0 Override \JViewLegacy::escape() instead.
*/
public function setEscape($spec)
{
\JLog::add(__METHOD__ . ' is deprecated. Override \JViewLegacy::escape() instead.', \JLog::WARNING, 'deprecated');
$this->_escape = $spec;
}
/**
* Adds to the stack of view script paths in LIFO order.
*
* @param mixed $path A directory path or an array of paths.
*
* @return void
*
* @since 3.0
*/
public function addTemplatePath($path)
{
$this->_addPath('template', $path);
}
/**
* Adds to the stack of helper script paths in LIFO order.
*
* @param mixed $path A directory path or an array of paths.
*
* @return void
*
* @since 3.0
*/
public function addHelperPath($path)
{
$this->_addPath('helper', $path);
}
/**
* Load a template file -- first look in the templates folder for an override
*
* @param string $tpl The name of the template source file; automatically searches the template paths and compiles as needed.
*
* @return string The output of the the template script.
*
* @since 3.0
* @throws \Exception
*/
public function loadTemplate($tpl = null)
{
// Clear prior output
$this->_output = null;
$template = \JFactory::getApplication()->getTemplate();
$layout = $this->getLayout();
$layoutTemplate = $this->getLayoutTemplate();
// Create the template file name based on the layout
$file = isset($tpl) ? $layout . '_' . $tpl : $layout;
// Clean the file name
$file = preg_replace('/[^A-Z0-9_\.-]/i', '', $file);
$tpl = isset($tpl) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $tpl) : $tpl;
// Load the language file for the template
$lang = \JFactory::getLanguage();
$lang->load('tpl_' . $template, JPATH_BASE, null, false, true)
|| $lang->load('tpl_' . $template, JPATH_THEMES . "/$template", null, false, true);
// Change the template folder if alternative layout is in different template
if (isset($layoutTemplate) && $layoutTemplate !== '_' && $layoutTemplate != $template)
{
$this->_path['template'] = str_replace(
JPATH_THEMES . DIRECTORY_SEPARATOR . $template,
JPATH_THEMES . DIRECTORY_SEPARATOR . $layoutTemplate,
$this->_path['template']
);
}
// Load the template script
jimport('joomla.filesystem.path');
$filetofind = $this->_createFileName('template', array('name' => $file));
$this->_template = \JPath::find($this->_path['template'], $filetofind);
// If alternate layout can't be found, fall back to default layout
if ($this->_template == false)
{
$filetofind = $this->_createFileName('', array('name' => 'default' . (isset($tpl) ? '_' . $tpl : $tpl)));
$this->_template = \JPath::find($this->_path['template'], $filetofind);
}
if ($this->_template != false)
{
// Unset so as not to introduce into template scope
unset($tpl, $file);
// Never allow a 'this' property
if (isset($this->this))
{
unset($this->this);
}
// Start capturing output into a buffer
ob_start();
// Include the requested template filename in the local scope
// (this will execute the view logic).
include $this->_template;
// Done with the requested template; get the buffer and
// clear it.
$this->_output = ob_get_contents();
ob_end_clean();
return $this->_output;
}
else
{
throw new \Exception(\JText::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $file), 500);
}
}
/**
* Load a helper file
*
* @param string $hlp The name of the helper source file automatically searches the helper paths and compiles as needed.
*
* @return void
*
* @since 3.0
*/
public function loadHelper($hlp = null)
{
// Clean the file name
$file = preg_replace('/[^A-Z0-9_\.-]/i', '', $hlp);
// Load the template script
jimport('joomla.filesystem.path');
$helper = \JPath::find($this->_path['helper'], $this->_createFileName('helper', array('name' => $file)));
if ($helper != false)
{
// Include the requested template filename in the local scope
include_once $helper;
}
}
/**
* Sets an entire array of search paths for templates or resources.
*
* @param string $type The type of path to set, typically 'template'.
* @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
*
* @since 3.0
*/
protected function _setPath($type, $path)
{
$component = \JApplicationHelper::getComponentName();
$app = \JFactory::getApplication();
// Clear out the prior search dirs
$this->_path[$type] = array();
// Actually add the user-specified directories
$this->_addPath($type, $path);
// Always add the fallback directories as last resort
switch (strtolower($type))
{
case 'template':
// Set the alternative template search dir
if (isset($app))
{
$component = preg_replace('/[^A-Z0-9_\.-]/i', '', $component);
$fallback = JPATH_THEMES . '/' . $app->getTemplate() . '/html/' . $component . '/' . $this->getName();
$this->_addPath('template', $fallback);
}
break;
}
}
/**
* Adds to the search path for templates and resources.
*
* @param string $type The type of path to add.
* @param mixed $path The directory or stream, or an array of either, to search.
*
* @return void
*
* @since 3.0
*/
protected function _addPath($type, $path)
{
jimport('joomla.filesystem.path');
// Loop through the path directories
foreach ((array) $path as $dir)
{
// Clean up the path
$dir = \JPath::clean($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->_path[$type], $dir);
}
}
/**
* Create the filename for a resource
*
* @param string $type The resource type to create the filename for
* @param array $parts An associative array of filename information
*
* @return string The filename
*
* @since 3.0
*/
protected function _createFileName($type, $parts = array())
{
switch ($type)
{
case 'template':
$filename = strtolower($parts['name']) . '.' . $this->_layoutExt;
break;
default:
$filename = strtolower($parts['name']) . '.php';
break;
}
return $filename;
}
/**
* Returns the form object
*
* @return mixed A \JForm object on success, false on failure
*
* @since 3.2
*/
public function getForm()
{
if (!is_object($this->form))
{
$this->form = $this->get('Form');
}
return $this->form;
}
/**
* Sets the document title according to Global Configuration options
*
* @param string $title The page title
*
* @return void
*
* @since 3.6
*/
public function setDocumentTitle($title)
{
$app = \JFactory::getApplication();
// Check for empty title and add site name if param is set
if (empty($title))
{
$title = $app->get('sitename');
}
elseif ($app->get('sitename_pagetitles', 0) == 1)
{
$title = \JText::sprintf('JPAGETITLE', $app->get('sitename'), $title);
}
elseif ($app->get('sitename_pagetitles', 0) == 2)
{
$title = \JText::sprintf('JPAGETITLE', $title, $app->get('sitename'));
}
$this->document->setTitle($title);
}
}
CategoryView.php 0000644 00000017762 15235157362 0007713 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\MVC\View;
defined('JPATH_PLATFORM') or die;
/**
* Base HTML View class for the a Category list
*
* @since 3.2
*/
class CategoryView extends HtmlView
{
/**
* State data
*
* @var \Joomla\Registry\Registry
* @since 3.2
*/
protected $state;
/**
* Category items data
*
* @var array
* @since 3.2
*/
protected $items;
/**
* The category model object for this category
*
* @var \JModelCategory
* @since 3.2
*/
protected $category;
/**
* The list of other categories for this extension.
*
* @var array
* @since 3.2
*/
protected $categories;
/**
* Pagination object
*
* @var \JPagination
* @since 3.2
*/
protected $pagination;
/**
* Child objects
*
* @var array
* @since 3.2
*/
protected $children;
/**
* The name of the extension for the category
*
* @var string
* @since 3.2
*/
protected $extension;
/**
* The name of the view to link individual items to
*
* @var string
* @since 3.2
*/
protected $viewName;
/**
* Default title to use for page title
*
* @var string
* @since 3.2
*/
protected $defaultPageTitle;
/**
* Whether to run the standard Joomla plugin events.
* Off by default for b/c
*
* @var bool
* @since 3.5
*/
protected $runPlugins = false;
/**
* Method with common display elements used in category list displays
*
* @return boolean|\JException|void Boolean false or \JException instance on error, nothing otherwise
*
* @since 3.2
*/
public function commonCategoryDisplay()
{
$app = \JFactory::getApplication();
$user = \JFactory::getUser();
$params = $app->getParams();
// Get some data from the models
$model = $this->getModel();
$paramsModel = $model->getState('params');
$paramsModel->set('check_access_rights', 0);
$model->setState('params', $paramsModel);
$state = $this->get('State');
$category = $this->get('Category');
$children = $this->get('Children');
$parent = $this->get('Parent');
if ($category == false)
{
return \JError::raiseError(404, \JText::_('JGLOBAL_CATEGORY_NOT_FOUND'));
}
if ($parent == false)
{
return \JError::raiseError(404, \JText::_('JGLOBAL_CATEGORY_NOT_FOUND'));
}
// Check whether category access level allows access.
$groups = $user->getAuthorisedViewLevels();
if (!in_array($category->access, $groups))
{
return \JError::raiseError(403, \JText::_('JERROR_ALERTNOAUTHOR'));
}
$items = $this->get('Items');
$pagination = $this->get('Pagination');
// Check for errors.
if (count($errors = $this->get('Errors')))
{
\JError::raiseError(500, implode("\n", $errors));
return false;
}
// Setup the category parameters.
$cparams = $category->getParams();
$category->params = clone $params;
$category->params->merge($cparams);
$children = array($category->id => $children);
// Escape strings for HTML output
$this->pageclass_sfx = htmlspecialchars($params->get('pageclass_sfx', ''));
if ($this->runPlugins)
{
\JPluginHelper::importPlugin('content');
foreach ($items as $itemElement)
{
$itemElement = (object) $itemElement;
$itemElement->event = new \stdClass;
// For some plugins.
!empty($itemElement->description) ? $itemElement->text = $itemElement->description : $itemElement->text = null;
$dispatcher = \JEventDispatcher::getInstance();
$dispatcher->trigger('onContentPrepare', array($this->extension . '.category', &$itemElement, &$itemElement->params, 0));
$results = $dispatcher->trigger('onContentAfterTitle', array($this->extension . '.category', &$itemElement, &$itemElement->core_params, 0));
$itemElement->event->afterDisplayTitle = trim(implode("\n", $results));
$results = $dispatcher->trigger('onContentBeforeDisplay', array($this->extension . '.category', &$itemElement, &$itemElement->core_params, 0));
$itemElement->event->beforeDisplayContent = trim(implode("\n", $results));
$results = $dispatcher->trigger('onContentAfterDisplay', array($this->extension . '.category', &$itemElement, &$itemElement->core_params, 0));
$itemElement->event->afterDisplayContent = trim(implode("\n", $results));
if ($itemElement->text)
{
$itemElement->description = $itemElement->text;
}
}
}
$maxLevel = $params->get('maxLevel', -1) < 0 ? PHP_INT_MAX : $params->get('maxLevel', PHP_INT_MAX);
$this->maxLevel = &$maxLevel;
$this->state = &$state;
$this->items = &$items;
$this->category = &$category;
$this->children = &$children;
$this->params = &$params;
$this->parent = &$parent;
$this->pagination = &$pagination;
$this->user = &$user;
// Check for layout override only if this is not the active menu item
// If it is the active menu item, then the view and category id will match
$active = $app->getMenu()->getActive();
if ($active
&& $active->component == $this->extension
&& isset($active->query['view'], $active->query['id'])
&& $active->query['view'] == 'category'
&& $active->query['id'] == $this->category->id)
{
if (isset($active->query['layout']))
{
$this->setLayout($active->query['layout']);
}
}
elseif ($layout = $category->params->get('category_layout'))
{
$this->setLayout($layout);
}
$this->category->tags = new \JHelperTags;
$this->category->tags->getItemTags($this->extension . '.category', $this->category->id);
}
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @since 3.2
*/
public function display($tpl = null)
{
$this->prepareDocument();
return parent::display($tpl);
}
/**
* Method to prepares the document
*
* @return void
*
* @since 3.2
*/
protected function prepareDocument()
{
$app = \JFactory::getApplication();
$menus = $app->getMenu();
$this->pathway = $app->getPathway();
$title = null;
// Because the application sets a default page title, we need to get it from the menu item itself
$this->menu = $menus->getActive();
if ($this->menu)
{
$this->params->def('page_heading', $this->params->get('page_title', $this->menu->title));
}
else
{
$this->params->def('page_heading', \JText::_($this->defaultPageTitle));
}
$title = $this->params->get('page_title', '');
if (empty($title))
{
$title = $app->get('sitename');
}
elseif ($app->get('sitename_pagetitles', 0) == 1)
{
$title = \JText::sprintf('JPAGETITLE', $app->get('sitename'), $title);
}
elseif ($app->get('sitename_pagetitles', 0) == 2)
{
$title = \JText::sprintf('JPAGETITLE', $title, $app->get('sitename'));
}
$this->document->setTitle($title);
if ($this->params->get('menu-meta_description'))
{
$this->document->setDescription($this->params->get('menu-meta_description'));
}
if ($this->params->get('menu-meta_keywords'))
{
$this->document->setMetadata('keywords', $this->params->get('menu-meta_keywords'));
}
if ($this->params->get('robots'))
{
$this->document->setMetadata('robots', $this->params->get('robots'));
}
}
/**
* Method to add an alternative feed link to a category layout.
*
* @return void
*
* @since 3.2
*/
protected function addFeed()
{
if ($this->params->get('show_feed_link', 1) == 1)
{
$link = '&format=feed&limitstart=';
$attribs = array('type' => 'application/rss+xml', 'title' => 'RSS 2.0');
$this->document->addHeadLink(\JRoute::_($link . '&type=rss'), 'alternate', 'rel', $attribs);
$attribs = array('type' => 'application/atom+xml', 'title' => 'Atom 1.0');
$this->document->addHeadLink(\JRoute::_($link . '&type=atom'), 'alternate', 'rel', $attribs);
}
}
}
CategoryFeedView.php 0000644 00000007712 15235157362 0010471 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\MVC\View;
defined('JPATH_PLATFORM') or die;
/**
* Base feed View class for a category
*
* @since 3.2
*/
class CategoryFeedView extends HtmlView
{
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @since 3.2
*/
public function display($tpl = null)
{
$app = \JFactory::getApplication();
$document = \JFactory::getDocument();
$extension = $app->input->getString('option');
$contentType = $extension . '.' . $this->viewName;
$ucmType = new \JUcmType;
$ucmRow = $ucmType->getTypeByAlias($contentType);
$ucmMapCommon = json_decode($ucmRow->field_mappings)->common;
$createdField = null;
$titleField = null;
if (is_object($ucmMapCommon))
{
$createdField = $ucmMapCommon->core_created_time;
$titleField = $ucmMapCommon->core_title;
}
elseif (is_array($ucmMapCommon))
{
$createdField = $ucmMapCommon[0]->core_created_time;
$titleField = $ucmMapCommon[0]->core_title;
}
$document->link = \JRoute::_(\JHelperRoute::getCategoryRoute($app->input->getInt('id'), $language = 0, $extension));
$app->input->set('limit', $app->get('feed_limit'));
$siteEmail = $app->get('mailfrom');
$fromName = $app->get('fromname');
$feedEmail = $app->get('feed_email', 'none');
$document->editor = $fromName;
if ($feedEmail !== 'none')
{
$document->editorEmail = $siteEmail;
}
// Get some data from the model
$items = $this->get('Items');
$category = $this->get('Category');
// Don't display feed if category id missing or non existent
if ($category == false || $category->alias === 'root')
{
return \JError::raiseError(404, \JText::_('JGLOBAL_CATEGORY_NOT_FOUND'));
}
foreach ($items as $item)
{
$this->reconcileNames($item);
// Strip html from feed item title
if ($titleField)
{
$title = $this->escape($item->$titleField);
$title = html_entity_decode($title, ENT_COMPAT, 'UTF-8');
}
else
{
$title = '';
}
// URL link to article
$router = new \JHelperRoute;
$link = \JRoute::_($router->getRoute($item->id, $contentType, null, null, $item->catid));
// Strip HTML from feed item description text.
$description = $item->description;
$author = $item->created_by_alias ?: $item->author;
$categoryTitle = isset($item->category_title) ? $item->category_title : $category->title;
if ($createdField)
{
$date = isset($item->$createdField) ? date('r', strtotime($item->$createdField)) : '';
}
else
{
$date = '';
}
// Load individual item creator class.
$feeditem = new \JFeedItem;
$feeditem->title = $title;
$feeditem->link = $link;
$feeditem->description = $description;
$feeditem->date = $date;
$feeditem->category = $categoryTitle;
$feeditem->author = $author;
// We don't have the author email so we have to use site in both cases.
if ($feedEmail === 'site')
{
$feeditem->authorEmail = $siteEmail;
}
elseif ($feedEmail === 'author')
{
$feeditem->authorEmail = $item->author_email;
}
// Loads item information into RSS array
$document->addItem($feeditem);
}
}
/**
* Method to reconcile non standard names from components to usage in this class.
* Typically overridden in the component feed view class.
*
* @param object $item The item for a feed, an element of the $items array.
*
* @return void
*
* @since 3.2
*/
protected function reconcileNames($item)
{
if (!property_exists($item, 'title') && property_exists($item, 'name'))
{
$item->title = $item->name;
}
}
}
CategoriesView.php 0000644 00000006436 15235157362 0010217 0 ustar 00 <?php
/**
* Joomla! Content Management System
*
* @copyright (C) 2013 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\CMS\MVC\View;
defined('JPATH_PLATFORM') or die;
/**
* Categories view base class.
*
* @since 3.2
*/
class CategoriesView extends HtmlView
{
/**
* State data
*
* @var \Joomla\Registry\Registry
* @since 3.2
*/
protected $state;
/**
* Category items data
*
* @var array
* @since 3.2
*/
protected $items;
/**
* Language key for default page heading
*
* @var string
* @since 3.2
*/
protected $pageHeading;
/**
* Execute and display a template script.
*
* @param string $tpl The name of the template file to parse; automatically searches through the template paths.
*
* @return mixed A string if successful, otherwise an Error object.
*
* @since 3.2
*/
public function display($tpl = null)
{
$state = $this->get('State');
$items = $this->get('Items');
$parent = $this->get('Parent');
$app = \JFactory::getApplication();
// Check for errors.
if (count($errors = $this->get('Errors')))
{
$app->enqueueMessage($errors, 'error');
return false;
}
if ($items === false)
{
$app->enqueueMessage(\JText::_('JGLOBAL_CATEGORY_NOT_FOUND'), 'error');
return false;
}
if ($parent == false)
{
$app->enqueueMessage(\JText::_('JGLOBAL_CATEGORY_NOT_FOUND'), 'error');
return false;
}
$params = &$state->params;
$items = array($parent->id => $items);
// Escape strings for HTML output
$this->pageclass_sfx = htmlspecialchars($params->get('pageclass_sfx', ''), ENT_COMPAT, 'UTF-8');
$this->maxLevelcat = $params->get('maxLevelcat', -1) < 0 ? PHP_INT_MAX : $params->get('maxLevelcat', PHP_INT_MAX);
$this->params = &$params;
$this->parent = &$parent;
$this->items = &$items;
$this->prepareDocument();
return parent::display($tpl);
}
/**
* Prepares the document
*
* @return void
*
* @since 3.2
*/
protected function prepareDocument()
{
$app = \JFactory::getApplication();
$menus = $app->getMenu();
// Because the application sets a default page title, we need to get it from the menu item itself
$menu = $menus->getActive();
if ($menu)
{
$this->params->def('page_heading', $this->params->get('page_title', $menu->title));
}
else
{
$this->params->def('page_heading', \JText::_($this->pageHeading));
}
$title = $this->params->get('page_title', '');
if (empty($title))
{
$title = $app->get('sitename');
}
elseif ($app->get('sitename_pagetitles', 0) == 1)
{
$title = \JText::sprintf('JPAGETITLE', $app->get('sitename'), $title);
}
elseif ($app->get('sitename_pagetitles', 0) == 2)
{
$title = \JText::sprintf('JPAGETITLE', $title, $app->get('sitename'));
}
$this->document->setTitle($title);
if ($this->params->get('menu-meta_description'))
{
$this->document->setDescription($this->params->get('menu-meta_description'));
}
if ($this->params->get('menu-meta_keywords'))
{
$this->document->setMetadata('keywords', $this->params->get('menu-meta_keywords'));
}
if ($this->params->get('robots'))
{
$this->document->setMetadata('robots', $this->params->get('robots'));
}
}
}
DataView/Form.php 0000644 00000004500 15235407750 0007673 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\View\DataView;
use FOF30\Form\Form as FormObject;
use FOF30\Model\DataModel;
use FOF30\Render\RenderInterface;
defined('_JEXEC') or die;
/**
* Class Form
* @package FOF30\View\DataView
*
* @deprecated 3.1 Support for XML forms will be removed in FOF 4
*/
class Form extends Html implements DataViewInterface
{
/** @var FormObject The form to render */
protected $form;
/**
* Displays the view
*
* @param string $tpl The template to use
*
* @return boolean|null False if we can't render anything
*
* @throws \Exception
*/
public function display($tpl = null)
{
/** @var DataModel $model */
$model = $this->getModel();
// Get the form
$this->form = $model->getForm();
$this->form->setModel($model);
$this->form->setView($this);
$eventName = 'onBefore' . ucfirst($this->doTask);
$this->triggerEvent($eventName, array($tpl));
$preRenderResult = '';
if ($this->doPreRender)
{
@ob_start();
$this->preRender();
$preRenderResult = @ob_get_contents();
@ob_end_clean();
}
try
{
$templateResult = $this->loadTemplate($tpl);
}
catch (\Exception $e)
{
$templateResult = $this->getRenderedForm();
}
$eventName = 'onAfter' . ucfirst($this->doTask);
$this->triggerEvent($eventName, array($tpl));
if (is_object($templateResult) && ($templateResult instanceof \Exception))
{
throw $templateResult;
}
echo $preRenderResult . $templateResult;
if ($this->doPostRender)
{
$this->postRender();
}
return true;
}
/**
* Returns the HTML rendering of the F0FForm attached to this view. Very
* useful for customising a form page without having to meticulously hand-
* code the entire form.
*
* @return string The HTML of the rendered form
*/
public function getRenderedForm()
{
$html = '';
$renderer = $this->container->renderer;
if ($renderer instanceof RenderInterface)
{
// Load CSS and Javascript files defined in the form
$this->form->loadCSSFiles();
$this->form->loadJSFiles();
/** @var DataModel $model */
$model = $this->getModel();
// Get the form's HTML
$html = $renderer->renderForm($this->form, $model);
}
return $html;
}
}