Your IP : 216.73.217.68


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

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

namespace  FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\View\View;

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

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

namespace FOF40\View\Engine;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$precompiledRelativePath = implode(DIRECTORY_SEPARATOR, $pathParts);

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

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

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

		return md5($path);
	}

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

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

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

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

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

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

		return $fileTime <= $cacheTime;
	}

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

		return file_get_contents($cachePath);
	}

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

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

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

		return false;
	}

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

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

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

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

namespace  FOF40\View\Engine;

defined('_JEXEC') || die;

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

namespace  FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\View\View;

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

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

namespace FOF40\View\Engine;

defined('_JEXEC') || die;

use FOF40\View\View;

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

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

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

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

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

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

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

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

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

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

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

namespace  FOF40\View\Exception;

defined('_JEXEC') || die;

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

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

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

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

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

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

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

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

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

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

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

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

namespace FOF40\View\Exception;

defined('_JEXEC') || die;

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

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

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

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

namespace FOF40\View\Compiler;

defined('_JEXEC') || die;

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

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

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

namespace FOF40\View\Compiler;

defined('_JEXEC') || die;

use FOF40\Container\Container;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$this->usingTokenizer = true;
	}

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

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

		return $this->isCacheable;
	}

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

		$this->footer = [];

		$fileData = @file_get_contents($path);

		$this->setPath($path);

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


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

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

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

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

			$result .= $value;
		}

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

		return $result;
	}

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

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

			return;
		}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		return $content;
	}

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

		return $value;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$this->footer[] = $data;

		return '';
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

namespace  FOF40\View;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$uris = array();

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

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

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

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

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

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

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

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

		return array_unique($uris);
	}

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

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

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

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

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

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

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

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

		return $parts;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$this->sidePrefix = $sidePrefix;
	}


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

namespace FOF40\View;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$this->config = $config;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		return null;
	}

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

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

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

		return $this->name;
	}

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

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

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

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

				return $result;
			}

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

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

			return $result;
		}

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

		return $default;
	}

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

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

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

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

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

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

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

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

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

		$preRenderResult = '';

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

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

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

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

		echo $preRenderResult . $templateResult;

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

		return true;
	}

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

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

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

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

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

		return $previous;
	}

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

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

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

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

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

		return $result;
	}

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

		$layoutTemplate = $this->getLayoutTemplate();

		$extraPaths = [];

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

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

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

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

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

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

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

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

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

		return $contents;
	}

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

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

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

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

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

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

			return $result;
		}

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

			return $result;
		}

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

			return $result;
		}

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

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

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

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

			throw new EmptyStack();
		}

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

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

		return $last;
	}

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

			throw new EmptyStack();
		}

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

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

		return $last;
	}

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

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

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

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

		$this->sectionStack = [];
	}

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

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

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

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

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

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

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

		return $this;
	}

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

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

		return $this;
	}

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

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

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

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

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

		return $this;
	}

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

		return $this;
	}

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

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

		return $this;
	}

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

		return $this;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$obLevel = ob_get_level();

		ob_start();

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

		return ob_get_clean();
	}

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

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

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

		throw $newException;
	}

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

		throw new UnrecognisedExtension($path);
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

	}

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

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

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

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

namespace FOF40\View\DataView;

defined('_JEXEC') or die;

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$hasFailed = false;

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

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

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

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

			reset($items);

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

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

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

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

				$keys = $temp;
			}

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

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

					$csv[] = $k;
				}

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

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

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

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

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

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

					$csv[] = $v;
				}

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

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

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

namespace FOF40\View\DataView;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		return (object) $permissions;
	}

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

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

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

		// Display limits
		$defaultLimit = 20;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

namespace FOF40\View\DataView;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

		return true;
	}

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

		$result = '';

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

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

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

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

		$hasFailed = false;

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

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

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

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

			$json = json_encode($result, JSON_PRETTY_PRINT);

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

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

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

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

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

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

		$result = '';

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


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

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

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

		$hasFailed = false;

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

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

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

			$json = json_encode($data, JSON_PRETTY_PRINT);

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

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

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

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

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

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

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

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

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

namespace  FOF40\View\DataView;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

namespace FOF40\View\DataView;

defined('_JEXEC') || die;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		$document->setTitle($title);

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

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

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

		return $title;
	}

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

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

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

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

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

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

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

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

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

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

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

		parent::onBeforeAdd();
	}

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

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

defined('_JEXEC') || die;

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

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

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

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

	/** @var string|null  */
	public $step1url = null;
}FileFilters/Html.php000060400000010144152457233650010401 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\FileFilters;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\FileFilters;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use Akeeba\Engine\Factory;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\HTML\HTMLHelper as JHtml;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Uri\Uri as JUri;

class Html extends BaseView
{
	use ProfileIdAndName;

	/**
	 * SELECT element for choosing a database root
	 *
	 * @var  string
	 */
	public $root_select = '';

	/**
	 * List of database roots
	 *
	 * @var  array
	 */
	public $roots = [];

	/**
	 * @return  void
	 */
	public function onBeforeMain()
	{
		$this->container->template->addJS('media://com_akeeba/js/FileFilters.min.js', true, false, $this->container->mediaVersion);

		/** @var FileFilters $model */
		$model = $this->getModel();

		// Add custom submenus
		$task    = $model->getState('browse_task', 'normal', 'cmd');
		$toolbar = $this->container->toolbar;

		$toolbar->appendLink(
			JText::_('COM_AKEEBA_FILEFILTERS_LABEL_NORMALVIEW'),
			JUri::base() . 'index.php?option=com_akeeba&view=FileFilters&task=normal',
			($task == 'normal')
		);
		$toolbar->appendLink(
			JText::_('COM_AKEEBA_FILEFILTERS_LABEL_TABULARVIEW'),
			JUri::base() . 'index.php?option=com_akeeba&view=FileFilters&task=tabular',
			($task == 'tabular')
		);

		// Get a JSON representation of the available roots
		$filters   = Factory::getFilters();
		$root_info = $filters->getInclusions('dir');
		$roots     = [];
		$options   = [];

		if (!empty($root_info))
		{
			// Loop all dir definitions
			foreach ($root_info as $dir_definition)
			{
				if (is_null($dir_definition[1]))
				{
					// Site root definition has a null element 1. It is always pushed on top of the stack.
					array_unshift($roots, $dir_definition[0]);
				}
				else
				{
					$roots[] = $dir_definition[0];
				}

				$options[] = JHtml::_('select.option', $dir_definition[0], $dir_definition[0]);
			}
		}

		$siteRoot      = $roots[0];
		$selectOptions = [
			'list.select' => $siteRoot,
			'id'          => 'active_root',
		];

		$this->root_select = JHtml::_('select.genericlist', $options, 'root', $selectOptions);
		$this->roots       = $roots;
		$platform          = $this->container->platform;

		// Add script options
		$platform->addScriptOptions('akeeba.System.params.AjaxURL', 'index.php?option=com_akeeba&view=FileFilters&task=ajax');
		$platform->addScriptOptions('akeeba.Fsfilters.loadingGif', $this->container->template->parsePath('media://com_akeeba/icons/loading.gif'));

		switch ($task)
		{
			case 'normal':
			default:
				$this->setLayout('default');

				// Get a JSON representation of the directory data
				$platform->addScriptOptions('akeeba.FileFilters.guiData', $model->make_listing($siteRoot, [], ''));
				$platform->addScriptOptions('akeeba.FileFilters.viewType', "list");

				break;

			case 'tabular':
				$this->setLayout('tabular');

				// Get a JSON representation of the tabular filter data
				$platform->addScriptOptions('akeeba.FileFilters.guiData', $model->get_filters($siteRoot));
				$platform->addScriptOptions('akeeba.FileFilters.viewType', "tabular");

				break;
		}

		// Push translations
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIROOT');
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIERRORFILTER');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_DIRECTORIES');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPFILES');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPDIRS');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_FILES');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_DIRECTORIES_ALL');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPFILES_ALL');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPDIRS_ALL');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_FILES_ALL');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_APPLYTOALLDIRS');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_APPLYTOALLFILES');

		$this->getProfileIdAndName();
	}

}
errorhandler.php000060400000021722152457233650007760 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

// Protect from unauthorized access
defined('_JEXEC') or die();

/** @var Throwable $e */
/** @var string $title */
/** @var bool $isPro */

$code = $e->getCode();
$code = !empty($code) ? $code : 500;

$app  = class_exists('\Joomla\CMS\Factory') ? \Joomla\CMS\Factory::getApplication() : \JFactory::getApplication();

$user30 = (class_exists('JFactory') && method_exists('JFactory', 'getUser')) ? JFactory::getUser() : null;
$user38 = class_exists('\Joomla\CMS\Factory') && method_exists('\Joomla\CMS\Factory', 'getUser') ? \Joomla\CMS\Factory::getUser() : null;
$user40 = (is_object($app) && method_exists($app, 'getIdentity')) ? $app->getIdentity() : null;
$user = is_null($user40) ? $user38 : $user40;
$user = is_null($user40) ? $user30 : $user;
$isSuper = !is_null($user) && $user->authorise('core.admin');

$isFrontend   = class_exists('JApplicationSite') && ($app instanceof JApplicationSite);
$isFrontend   = $isFrontend || (class_exists('\Joomla\CMS\Application\SiteApplication') && ($app instanceof \Joomla\CMS\Application\SiteApplication));
$user         = $isFrontend ? (method_exists($app, 'getIdentity') ? $app->getIdentity() : JFactory::getUser()) : null;
$hideTheError = $isFrontend && !(defined('JDEBUG') && (JDEBUG == 1)) && !$isSuper;
$isPro        = !isset($isPro) ? false : $isPro;

// 403 and 404 are re-thrown
if (in_array($code, [403, 404]))
{
	throw $e;
}

if (version_compare(JVERSION, '4', 'lt'))
{
	$app->setHeader('HTTP/1.1', $code);
}
else
{
	// In Joomla 4 we have to use the "Status" header, otherwise we get a fatal error saying that
	// HTTP/1.1 is not a valid header
	$app->setHeader('Status', $code);
}

if (!$isFrontend)
{
	if (class_exists('\Joomla\CMS\Toolbar\ToolbarHelper'))
	{
		\Joomla\CMS\Toolbar\ToolbarHelper::title($title . ' <small>Unhandled Exception</small>');
	}
	else
	{
		JToolbarHelper::title($title . ' <small>Unhandled Exception</small>');
	}

}

?>

<?php if ($hideTheError): ?>
	<h1>The application has stopped responding</h1>
	<p>
		Please contact the administrator of the site and let them know of this error and what you were doing when this
		happened.
	</p>
	<?php return true; endif; ?>

<h1><?php echo $title ?> - An unhandled Exception has been detected</h1>
<h3>
	<?php if (version_compare(JVERSION, '3.999.999', 'le')): ?>
		<span class="label label-danger"><?php echo htmlentities($code) ?></span> <?php echo htmlentities($e->getMessage()) ?>
	<?php else: ?>
		<span class="badge badge-danger"><?php echo htmlentities($code) ?></span> <?php echo htmlentities($e->getMessage()) ?>
	<?php endif; ?>
</h3>
<p>
	File <code><?php echo htmlentities(str_ireplace(JPATH_ROOT, '&lt;root&gt;', $e->getFile())) ?></code>
	Line <span class="label label-info"><?php echo (int) $e->getLine() ?></span>
</p>

<?php if ($isPro): ?>
	<div class="<?php if (version_compare(JVERSION, '3.999.999', 'le')):?>hero-unit<?php else: ?>alert alert-primary<?php endif; ?>">
		<p>
			<strong>Would you like us to help you faster?</strong>
		</p>
		<p>
			Save this page as PDF or HTML. When filing a support ticket please attach that PDF or HTML file.
		</p>
	</div>
	<p>
		<strong>Why do we need all that information?</strong> This information is an x-ray of your site at the time the
		error
		occurred. It lets us reproduce the issue or, if it's not a bug in our software, help you pinpoint the external
		reason which
		led to it.
	</p>
	<p>
		<strong>What about privacy?</strong>
		Attachments are private in our ticket system: only you and us can see them, <em>even if you file a public
			ticket</em>, and
		they are automatically deleted after a month.
	</p>
<?php endif; ?>

<hr />
<p>
	<span class="icon icon-warning-2"></span>
	<em>
		The content below this point is for developers and power users.
	</em>
</p>
<hr />

<p class="alert alert-warning">
	Joomla <?= JVERSION ?> – PHP <?= PHP_VERSION ?> on <?= PHP_OS ?>
</p>

<h3>Debug information</h3>
<p>
	Exception type: <code><?php echo htmlentities(get_class($e)) ?></code>
</p>
<pre><?php echo htmlentities($e->getTraceAsString()) ?></pre>

<h3>System information</h3>
<table class="table table-striped">
	<tr>
		<td>Operating System (reported by PHP)</td>
		<td><?php echo PHP_OS ?></td>
	</tr>
	<tr>
		<td>PHP version (as reported <em>by your server</em>)</td>
		<td><?php echo PHP_VERSION ?></td>
	</tr>
	<tr>
		<td>PHP Built On</td>
		<td><?php echo htmlentities(php_uname()); ?></td>
	</tr>
	<tr>
		<td>PHP SAPI</td>
		<td><?php echo PHP_SAPI ?></td>
	</tr>
	<tr>
		<td>Server identity</td>
		<td><?php echo htmlentities(isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : getenv('SERVER_SOFTWARE')) ?></td>
	</tr>
	<tr>
		<td>Browser identity</td>
		<td><?php echo htmlentities(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '') ?></td>
	</tr>
	<tr>
		<td>Joomla! version</td>
		<td><?php echo JVERSION ?></td>
	</tr>
	<?php
	$db = JFactory::getDbo();
	if (!is_null($db)):
	?>
	<tr>
		<td>Database driver name</td>
		<td><?php echo $db->getName() ?></td>
	</tr>
	<tr>
		<td>Database driver type</td>
		<td><?php echo $db->getServerType() ?></td>
	</tr>
	<tr>
		<td>Database server version</td>
		<td><?php echo $db->getVersion() ?></td>
	</tr>
	<tr>
		<td>Database collation</td>
		<td><?php echo $db->getCollation() ?></td>
	</tr>
	<tr>
		<td>Database connection collation</td>
		<td><?php echo $db->getConnectionCollation() ?></td>
	</tr>
	<?php endif; ?>
	<tr>
		<td>PHP Memory limit</td>
		<td><?php echo function_exists('ini_get') ? htmlentities(ini_get('memory_limit')) : 'N/A' ?></td>
	</tr>
	<tr>
		<td>Peak Memory usage</td>
		<td><?php echo function_exists('memory_get_peak_usage') ? sprintf('%0.2fM', (memory_get_peak_usage() / 1024 / 1024)) : 'N/A' ?></td>
	</tr>
	<tr>
		<td>PHP Timeout (seconds)</td>
		<td><?php echo function_exists('ini_get') ? htmlentities(ini_get('max_execution_time')) : 'N/A' ?></td>
	</tr>
</table>

<h3>Request information</h3>
<h4>$_GET</h4>
<pre><?php echo htmlentities(print_r($_GET, true)) ?></pre>
<h4>$_POST</h4>
<pre><?php echo htmlentities(print_r($_POST, true)) ?></pre>
<h4>$_COOKIE</h4>
<pre><?php echo htmlentities(print_r($_COOKIE, true)) ?></pre>
<h4>$_REQUEST</h4>
<pre><?php echo htmlentities(print_r($_REQUEST, true)) ?></pre>

<h3>Session state</h3>
<pre><?php
	if (version_compare(JVERSION, '4', 'lt'))
	{
		echo htmlentities(print_r($app->getSession()->getData()->toArray(), true));
	}
	else
	{
		echo htmlentities(print_r($app->getSession()->all(), true));
	}
	?></pre>

<?php
if (version_compare(JVERSION, '3.999.999', 'le'))
{
	if (!include_once(JPATH_ADMINISTRATOR . '/components/com_admin/models/sysinfo.php'))
	{
		return;
	}

	$model       = new AdminModelSysInfo();
}
else
{
	try
	{
		/** @var MVCFactoryInterface $factory */
		$factory = $app->bootComponent('com_admin')->getMVCFactory();
		/** @var \Joomla\Component\Admin\Administrator\Model\SysinfoModel $model */
		$model = $factory->createModel('Sysinfo', 'Administrator');
	}
	catch (Exception $e)
	{
		return;
	}
}

$directories = $model->getDirectory();

try
{
	$extensions = $model->getExtensions();
}
catch (Exception $e)
{
	$extension = [];
}

$phpSettings = $model->getPhpSettings();
$hasPHPInfo  = $model->phpinfoEnabled();
?>

<h3>PHP Settings</h3>
<table class="table table-striped">
	<?php foreach ($phpSettings as $k => $v): ?>
		<tr>
			<td><?php echo $k ?></td>
			<td><?php echo htmlentities(print_r($v, true)) ?></td>
		</tr>
	<?php endforeach; ?>
</table>

<?php if ($hasPHPInfo):
	$phpInfo = $model->getPhpInfoArray(); ?>
	<h3>Loaded PHP Extensions</h3>
	<table class="table table-striped">
		<?php foreach ($phpInfo as $section => $data):
			if ($section == 'Core')
			{
				continue;
			} ?>
			<tr>
				<td><?php echo htmlentities($section) ?></td>
				<td>
					<?php if (in_array($section, ['curl', 'openssl', 'ssh2', 'ftp', 'session', 'tokenizer'])): ?>
						<pre><?php echo htmlentities(print_r($data, true)) ?></pre>
					<?php endif; ?>
				</td>
			</tr>
		<?php endforeach; ?>
	</table>
<?php endif; ?>

<h3>Enabled Extensions</h3>
<table class="table table-striped">
	<?php foreach ($extensions as $extension => $info):
		if (strtoupper($info['state']) != 'ENABLED')
		{
			continue;
		} ?>
		<tr>
			<td><?php echo htmlentities($extension) ?></td>
			<td><?php echo htmlentities($info['version']) ?></td>
			<td><?php echo htmlentities($info['type']) ?></td>
			<td><?php echo htmlentities($info['author']) ?></td>
			<td><?php echo htmlentities($info['authorUrl']) ?></td>
		</tr>
	<?php endforeach; ?>
</table>

<h3>Directory Status</h3>
<table class="table table-striped">
	<?php foreach ($directories as $k => $v): ?>
		<tr>
			<td>
				<?php echo htmlentities($k) ?>
				<?php echo !empty($v['message']) ? "[{$v['message']}]" : '' ?>
			</td>
			<td>
				<?php if ($v['writable']): ?>
					<span class="label label-success">Writeable</span>
				<?php else: ?>
					<span class="label label-danger">Unwriteable</span>
				<?php endif; ?>
			</td>
		</tr>
	<?php endforeach; ?>
</table>ControlPanel/Html.php000060400000021444152457233650010576 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\ControlPanel;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Helper\Status;
use Akeeba\Backup\Admin\Model\ControlPanel;
use Akeeba\Backup\Admin\Model\UsageStatistics;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileList;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use FOF40\View\DataView\Html as BaseView;

class Html extends BaseView
{
	use ProfileList, ProfileIdAndName;

	/**
	 * List of profiles to display as Quick Icons in the control panel page
	 *
	 * @var   array  Array of stdClass objects
	 */
	public $quickIconProfiles = [];

	/**
	 * The HTML for the backup status cell
	 *
	 * @var   string
	 */
	public $statusCell = '';

	/**
	 * HTML for the warnings (status details)
	 *
	 * @var   string
	 */
	public $detailsCell = '';

	/**
	 * Details of the latest backup as HTML
	 *
	 * @var   string
	 */
	public $latestBackupCell = '';

	/**
	 * Do I have to ask the user to fix the permissions?
	 *
	 * @var   bool
	 */
	public $areMediaPermissionsFixed = false;

	/**
	 * Do I have to ask the user to provide a Download ID?
	 *
	 * @var   bool
	 */
	public $needsDownloadID = false;

	/**
	 * Did a Core edition user provide a Download ID instead of installing Akeeba Backup Professional?
	 *
	 * @var   bool
	 */
	public $coreWarningForDownloadID = false;

	/**
	 * Our extension ID
	 *
	 * @var   int
	 */
	public $extension_id = 0;

	/**
	 * Should I have the browser ask for desktop notification permissions?
	 *
	 * @var   bool
	 */
	public $desktopNotifications = false;

	/**
	 * If anonymous statistics collection is enabled and we have to collect statistics this will include the HTML for
	 * the IFRAME that performs the anonymous stats collection.
	 *
	 * @var   string
	 */
	public $statsIframe = '';

	/**
	 * If front-end backup is enabled and the secret word has an issue (too insecure) we populate this variable
	 *
	 * @var  string
	 */
	public $frontEndSecretWordIssue = '';

	/**
	 * In case the existing Secret Word is insecure we generate a new one. This variable contains the new Secret Word.
	 *
	 * @var  string
	 */
	public $newSecretWord = '';

	/**
	 * Is the mbstring extension installed and enabled? This is required by Joomla and Akeeba Backup to correctly work
	 *
	 * @var  bool
	 */
	public $checkMbstring = true;

	/**
	 * The fancy formatted changelog of the component
	 *
	 * @var  string
	 */
	public $formattedChangelog = '';

	/**
	 * Should I pormpt the user ot run the configuration wizard?
	 *
	 * @var  bool
	 */
	public $promptForConfigurationWizard = false;

	/**
	 * How many warnings do I have to display?
	 *
	 * @var  int
	 */
	public $countWarnings = 0;

	/**
	 * Do I have stuck updates pending?
	 *
	 * @var  bool
	 */
	public $stuckUpdates = false;

	/**
	 * Cache the user permissions
	 *
	 * @var   array
	 *
	 * @since 5.3.0
	 */
	public $permissions = [];

	/**
	 * Timestamp when the Core user last dismissed the upsell to Pro
	 *
	 * @var   int
	 * @since 7.0.0
	 */
	public $lastUpsellDismiss = 0;

	/**
	 * Is the output directory under the site's root?
	 *
	 * @var   bool
	 * @since 7.0.3
	 */
	public $isOutputDirectoryUnderSiteRoot = false;

	/**
	 * Does the output directory have the expected security files?
	 *
	 * @var   bool
	 * @since 7.0.3
	 */
	public $hasOutputDirectorySecurityFiles = false;

	/**
	 * Executes before displaying the control panel page
	 */
	public function onBeforeMain()
	{
		/** @var ControlPanel $model */
		$model = $this->getModel();

		$statusHelper      = Status::getInstance();
		$this->statsIframe = '';

		try
		{
			/** @var UsageStatistics $usageStatsModel */
			$usageStatsModel = $this->container->factory->model('UsageStatistics')->tmpInstance();

			if (
				is_object($usageStatsModel)
				&& class_exists('Akeeba\\Backup\\Admin\\Model\\UsageStatistics')
				&& ($usageStatsModel instanceof UsageStatistics)
				&& method_exists($usageStatsModel, 'collectStatistics')
			)
			{
				$this->statsIframe = $usageStatsModel->collectStatistics(true);
			}
		}
		catch (\Exception $e)
		{
			// Don't give a crap if usage stats ain't loaded
		}

		$this->getProfileList();
		$this->getProfileIdAndName();

		$this->quickIconProfiles               = $model->getQuickIconProfiles();
		$this->statusCell                      = $statusHelper->getStatusCell();
		$this->detailsCell                     = $statusHelper->getQuirksCell();
		$this->latestBackupCell                = $statusHelper->getLatestBackupDetails();
		$this->areMediaPermissionsFixed        = $model->fixMediaPermissions();
		$this->checkMbstring                   = $model->checkMbstring();
		$this->needsDownloadID                 = $model->needsDownloadID() ? 1 : 0;
		$this->coreWarningForDownloadID        = $model->mustWarnAboutDownloadIDInCore();
		$this->extension_id                    = $model->getState('extension_id', 0, 'int');
		$this->frontEndSecretWordIssue         = $model->getFrontendSecretWordError();
		$this->newSecretWord                   = $this->container->platform->getSessionVar('newSecretWord', null, 'akeeba.cpanel');
		$this->desktopNotifications            = $this->container->params->get('desktop_notifications', '0') ? 1 : 0;
		$this->formattedChangelog              = $this->formatChangelog();
		$this->promptForConfigurationWizard    = Factory::getConfiguration()->get('akeeba.flag.confwiz', 0) == 0;
		$this->countWarnings                   = count(Factory::getConfigurationChecks()->getDetailedStatus());
		$this->stuckUpdates                    = ($this->container->params->get('updatedb', 0) == 1);
		$user                                  = $this->container->platform->getUser();
		$this->permissions                     = [
			'configure' => $user->authorise('akeeba.configure', 'com_akeeba'),
			'backup'    => $user->authorise('akeeba.backup', 'com_akeeba'),
			'download'  => $user->authorise('akeeba.download', 'com_akeeba'),
		];
		$this->isOutputDirectoryUnderSiteRoot  = $model->isOutputDirectoryUnderSiteRoot();
		$this->hasOutputDirectorySecurityFiles = $model->hasOutputDirectorySecurityFiles();

		$this->lastUpsellDismiss = $this->container->params->get('lastUpsellDismiss', 0);

		// Load the version constants
		Platform::getInstance()->load_version_defines();

		// Add the Javascript to the document
		$this->container->template->addJS('media://com_akeeba/js/ControlPanel.min.js', true, false, $this->container->mediaVersion);
		$this->addJSScriptOptions();
	}

	/**
	 * Adds inline Javascript to the document
	 */
	protected function addJSScriptOptions()
	{
		$platform = $this->container->platform;
		$platform->addScriptOptions('akeeba.System.notification.hasDesktopNotification', (bool)$this->desktopNotifications);
		$platform->addScriptOptions('akeeba.ControlPanel.needsDownloadID', (bool) $this->needsDownloadID);
		$platform->addScriptOptions('akeeba.ControlPanel.outputDirUnderSiteRoot', (bool) $this->isOutputDirectoryUnderSiteRoot);
		$platform->addScriptOptions('akeeba.ControlPanel.hasSecurityFiles', (bool) $this->hasOutputDirectorySecurityFiles);
	}

	protected function formatChangelog($onlyLast = false)
	{
		$ret   = '';
		$file  = $this->container->backEndPath . '/CHANGELOG.php';
		$lines = @file($file);

		if (empty($lines))
		{
			return $ret;
		}

		array_shift($lines);

		foreach ($lines as $line)
		{
			$line = trim($line);

			if (empty($line))
			{
				continue;
			}

			$type = substr($line, 0, 1);

			switch ($type)
			{
				case '=':
					continue 2;
					break;

				case '+':
					$ret .= "\t" . '<li><span class="akeeba-label--green">Added</span> ' . htmlentities(trim(substr($line, 2))) . "</li>\n";
					break;

				case '-':
					$ret .= "\t" . '<li><span class="akeeba-label--grey">Removed</span> ' . htmlentities(trim(substr($line, 2))) . "</li>\n";
					break;

				case '~':
				case '^':
					$ret .= "\t" . '<li><span class="akeeba-label--grey">Changed</span> ' . htmlentities(trim(substr($line, 2))) . "</li>\n";
					break;

				case '*':
					$ret .= "\t" . '<li><span class="akeeba-label--red">Security</span> ' . htmlentities(trim(substr($line, 2))) . "</li>\n";
					break;

				case '!':
					$ret .= "\t" . '<li><span class="akeeba-label--orange">Important</span> ' . htmlentities(trim(substr($line, 2))) . "</li>\n";
					break;

				case '#':
					$ret .= "\t" . '<li><span class="akeeba-label--teal">Fixed</span> ' . htmlentities(trim(substr($line, 2))) . "</li>\n";
					break;

				default:
					if (!empty($ret))
					{
						$ret .= "</ul>";
						if ($onlyLast)
						{
							return $ret;
						}
					}

					if (!$onlyLast)
					{
						$ret .= "<h4>$line</h4>\n";
					}
					$ret .= "<ul class=\"akeeba-changelog\">\n";

					break;
			}
		}

		return $ret;
	}
}
Browser/Html.php000060400000004525152457233650007622 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Browser;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\Browser;
use FOF40\View\DataView\Html as BaseView;

class Html extends BaseView
{
	/**
	 * Path to current folder (with variables such as [SITEROOT] replaced)
	 *
	 * @var  string
	 */
	public $folder = '';

	/**
	 * Path to current folder (WITHOUT variables such as [SITEROOT] replaced)
	 *
	 * @var  string
	 */
	public $folder_raw = '';

	/**
	 * Parent folder
	 *
	 * @var  string
	 */
	public $parent = '';

	/**
	 * Does the current folder exist in the filesystem?
	 *
	 * @var  bool
	 */
	public $exists = false;

	/**
	 * Is the current folder under the site's root directory? False means it's an off-site directory.
	 *
	 * @var  bool
	 */
	public $inRoot = false;

	/**
	 * Is the current folder restricted by open_basedir?
	 *
	 * @var  bool
	 */
	public $openbasedirRestricted = false;

	/**
	 * Is the current folder writable?
	 *
	 * @var  bool
	 */
	public $writable = false;

	/**
	 * Subdirectories
	 *
	 * @var  array
	 */
	public $subfolders = [];

	/**
	 * Breadcrumbs to display in the browser view
	 *
	 * @var  array
	 */
	public $breadcrumbs = [];

	protected function onBeforeMain()
	{
		// Load the view-specific Javascript
		$this->container->template->addJS('media://com_akeeba/js/Browser.min.js', true, false, $this->container->mediaVersion);

		/** @var Browser $model */
		$model = $this->getModel();

		// Pass the data from the model to the view template
		$this->folder                = $model->getState('folder', '', 'string');
		$this->folder_raw            = $model->getState('folder_raw', '', 'string');
		$this->parent                = $model->getState('parent', '', 'string');
		$this->exists                = $model->getState('exists', 0, 'boolean');
		$this->inRoot                = $model->getState('inRoot', 0, 'boolean');
		$this->openbasedirRestricted = $model->getState('openbasedirRestricted', 0, 'boolean');
		$this->writable              = $model->getState('writable', 0, 'boolean');
		$this->subfolders            = $model->getState('subfolders');
		$this->breadcrumbs           = $model->getState('breadcrumbs');
	}
}
fof.php000060400000005035152457233650006042 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

// Protect from unauthorized access
defined('_JEXEC') or die();

$tooLongAgo = (int) gmdate('Y') - 2015;
?>

<div style="margin: 1em">
	<h1>Akeeba Framework-on-Framework (FOF) version 3 could not be found on this site</h1>
	<hr/>
	<div class="alert alert-warning">
		<h2>
			This component requires the Akeeba FOF framework package to be installed on your site. Please go to <a
					href="https://www.akeeba.com/download/fof3.html">our download page</a> to download it, then install it on your site.
		</h2>
	</div>
	<hr/>
	<h4>Further information</h4>
	<p>
		FOF is a Joomla component framework. It's the low level code which sits between our Joomla! extensions and
		Joomla! itself. It is automatically installed when you install our extensions on your site.
	</p>
	<p>
		FOF can be missing from your site either because Joomla failed to install it or because you, another Super User,
		or another extension mistakenly uninstalled it.
	</p>
	<p>
		If it's missing, our components cannot talk to Joomla &mdash; or vice versa. Because of that they can not run.
		That's why you see this message.
	</p>
	<p>
		You do not have to worry about adding bloat to your site. FOF is very small. It will also be automatically
		uninstalled when you uninstall all components which depend on it.
	</p>
	<p>
		FOF is installed in the <code><?php echo JPATH_LIBRARIES . DIRECTORY_SEPARATOR?>/fof30</code> folder on your
		server. It appears in Joomla's Extensions, Manage page as <code>FOF30</code>. Please do not remove it from your
		site.
	</p>
<?php if (version_compare(JVERSION, '3.9999.9999', 'le')): ?>
	<h4>Why do I have multiple FOF entries in Joomla?</h4>
	<p>
		Joomla <?php echo JVERSION ?> includes an <em>old, obsolete</em> version of FOF - version 2.x. It is installed
		in the <code><?php echo JPATH_LIBRARIES . DIRECTORY_SEPARATOR ?>fof</code> folder on your server. It appears in
		Joomla's Extensions, Manage page as <code>FOF</code>. Please do not remove it from your site; Joomla needs it
		to function properly.
	</p>
	<p>
		We discontinued FOF 2.x in 2015 &mdash; that's <?php echo $tooLongAgo ?> years ago. Ever since, we replaced it
		with FOF 3.x. The two versions are incompatible with each other but both are required; FOF 2.x for Joomla!
		itself and FOF 3.x for our extensions. That's why you see both. You must not remove either of them or something
		will break!
	</p>
<?php endif; ?>
</div>
wrongphp.php000060400000026357152457233650007146 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

(defined('_JEXEC') || defined('WPINC') || defined('APATH_BASE') || defined('AKEEBA_COMMON_WRONGPHP') || defined('KICKSTART')) or die;

if (!function_exists('akeeba_common_wrongphp'))
{
	/**
	 * This function checks if you are using an obsolete PHP version. It returns and boolean status and optionally
	 * prints an error page if your PHP version is, indeed, too old.
	 *
	 * * minPHPVersion: minimum PHP version supported by this software, e.g. "7.2.0"
	 * * recommendedPHPVersion: recommended PHP version to use with this software, e.g. "7.3"
	 * * softwareName: human-readable software name, e.g. "Akeeba Example"
	 * * silentResutls: suppress error messages on old PHP version, just return false (default: TRUE)
	 * * longVersion: current PHP version, long format, e.g. "7.3.1-12ubuntu3.2". Skip to automatically determine.
	 * * shortVersion: current PHP version, short format, e.g. "7.3". Skip to automatically determine.
	 * * currentTimestamp: current UNIX timestamp. Skip to automatically determine.
	 *
	 * You need to provide at the very least the minPHPVersion, recommendedPHPVersion and softwareName.
	 *
	 * @param  array  $config
	 *
	 * @return bool  FALSE if your PHP version is too old. TRUE if your PHP version is still supported.
	 * @throws Exception
	 */
	function akeeba_common_wrongphp($config = array())
	{
		/**
		 * Format: version => [maintenance_date, eol_date]
		 *
		 * For versions older than 5.6 we use a fake maintenance_date because this information no longer exists on PHP's
		 * site and it's irrelevant anyway; these PHP versions are already EOL therefore we only use their EOL date.
		 */
		$phpDates = array(
			'3.0' => array('1990-01-01 00:00:00', '2000-10-20 00:00:00'),
			'4.0' => array('1990-01-01 00:00:00', '2001-06-23 00:00:00'),
			'4.1' => array('1990-01-01 00:00:00', '2002-03-12 00:00:00'),
			'4.2' => array('1990-01-01 00:00:00', '2002-09-06 00:00:00'),
			'4.3' => array('1990-01-01 00:00:00', '2005-03-31 00:00:00'),
			'4.4' => array('1990-01-01 00:00:00', '2008-08-07 00:00:00'),
			'5.0' => array('1990-01-01 00:00:00', '2005-09-05 00:00:00'),
			'5.1' => array('1990-01-01 00:00:00', '2006-08-24 00:00:00'),
			'5.2' => array('1990-01-01 00:00:00', '2011-01-11 00:00:00'),
			'5.3' => array('1990-01-01 00:00:00', '2014-08-14 00:00:00'),
			'5.4' => array('1990-01-01 00:00:00', '2015-09-03 00:00:00'),
			'5.5' => array('1990-01-01 00:00:00', '2016-07-10 00:00:00'),
			'5.6' => array('2017-01-10 00:00:00', '2018-12-31 00:00:00'),
			'7.0' => array('2018-01-01 00:00:00', '2019-01-10 00:00:00'),
			'7.1' => array('2018-12-01 00:00:00', '2019-12-01 00:00:00'),
			'7.2' => array('2019-11-30 00:00:00', '2020-11-30 00:00:00'),
			'7.3' => array('2020-12-06 00:00:00', '2021-12-06 00:00:00'),
			'7.4' => array('2021-11-28 00:00:00', '2022-11-28 00:00:00'),
			'8.0' => array('2022-11-26 00:00:00', '2023-11-26 00:00:00'),
		);

		// Make sure I have all necessary configuration variables
		$config = array_merge(array(
			'minPHPVersion'         => '7.2.0',
			'recommendedPHPVersion' => '7.3',
			'softwareName'          => 'This software',
			'silentResults'         => false,
			'longVersion'           => PHP_VERSION,
			'shortVersion'          => sprintf('%d.%d', PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
			'currentTimestamp'      => time(),
		), $config);

		// Selectively extract configuration variables. Do not use extract(), it's potentially dangerous.
		$minPHPVersion         = $config['minPHPVersion'];
		$recommendedPHPVersion = $config['recommendedPHPVersion'];
		$softwareName          = $config['softwareName'];
		$silentResults         = $config['silentResults'];
		$longVersion           = $config['longVersion'];
		$shortVersion          = $config['shortVersion'];
		$currentTimestamp      = $config['currentTimestamp'];

		if (!version_compare($longVersion, $minPHPVersion, 'lt'))
		{
			unset($minPHPVersion, $recommendedPHPVersion, $softwareName, $longVersion, $shortVersion, $phpDates,
				$silentResults, $currentTimestamp);

			return true;
		}

// Typically used in the frontend to not divulge any information about the server
		if ($silentResults)
		{
			return false;
		}

		/**
		 * Safe defaults for PHP versions older than 5.3.0.
		 *
		 * Older PHP versions don't even have support for DateTime so we need these defaults to prevent this warning script from
		 * bringing the site down with an error.
		 */
		$isEol      = true;
		$isAncient  = true;
		$isSecurity = false;
		$isCurrent  = false;

		$eolDateFormatted      = $phpDates[$shortVersion][1];
		$securityDateFormatted = $phpDates[$shortVersion][0];


		/**
		 * This can only work on PHP 5.2.0 or later
		 */
		if (version_compare($longVersion, '5.2.0', 'ge'))
		{
			$tzGmt        = new DateTimeZone('GMT');
			$securityDate = new DateTime($phpDates[$shortVersion][0], $tzGmt);
			$eolDate      = new DateTime($phpDates[$shortVersion][1], $tzGmt);

			/**
			 * Ancient:  This PHP version has reached end-of-life more than 2 years ago
			 * EOL:      This PHP version has reached end-of-life
			 * Security: This PHP version has reached the Security Support date but not the EOL date yet
			 * Current:  This PHP version is still in Active Support
			 */
			$isEol      = $eolDate->getTimestamp() <= $currentTimestamp;
			$isAncient  = $isEol && (($currentTimestamp - $eolDate->getTimestamp()) >= 63072000);
			$isSecurity = !$isEol && ($securityDate->getTimestamp() <= $currentTimestamp);
			$isCurrent  = !$isEol && !$isSecurity;

			$eolDateFormatted      = $eolDate->format('l, d F Y');
			$securityDateFormatted = $securityDate->format('l, d F Y');
		}

		$characterization = $isCurrent ? 'unsupported' : 'older';
		$characterization = $isEol ? 'obsolete' : $characterization;
		$characterization = $isAncient ? 'dangerously obsolete' : $characterization;

		?>

		<div style="margin: 1em">
			<p style="font-size: 180%; margin: 2em 1em; padding: 2em 1em; text-align: center; border: thin solid #f0ad4e; background-color: gold; font-weight: bold; border-radius: 0.25em">
				<?php echo $softwareName ?> requires PHP <?php echo $minPHPVersion ?> or later.
			</p>
			<h2><?php echo ucfirst($characterization) ?> PHP version <?php echo $longVersion ?> detected</h2>
			<hr />
			<p>
				We recommend that you upgrade your site to PHP <?php echo $recommendedPHPVersion ?> or later. If you are
				unsure how to do this, please ask your host.
			</p>
			<p>
				<a href="https://www.akeeba.com/how-do-version-numbers-work.html">Version numbers don't make
					sense?</a>
			</p>

			<hr />

			<?php if ($isAncient): ?>
				<h3>Urgent security advice</h3>

				<p>
					Your version of PHP, <?php echo $longVersion ?>, <a href="http://php.net/eol.php">has reached the end
						of its life</a> a <strong>very</strong> long time ago, namely on
					<?php echo $eolDateFormatted ?>. It has known security vulnerabilities which are used to
					compromise (“hack”) web servers. It is no longer safe using it in production. You are <strong>VERY
						STRONGLY</strong> advised to upgrade your server to a <a
							href="https://www.php.net/supported-versions.php">supported PHP version</a> as soon as possible.
				</p>
			<?php elseif ($isEol): ?>
				<h3>Security advice</h3>

				<p>
					Your version of PHP, <?php echo $longVersion ?>, <a href="http://php.net/eol.php">has reached the end
						of its life</a> on <?php echo $eolDateFormatted ?>. End-of-life PHP versions may have
					as-yet-undiscovered security vulnerabilities which can be used to compromise (“hack”) your site. It is
					no
					longer safe using it in production, even if your host or your Linux distribution claim otherwise – the
					PHP
					developers themselves have said time over time that not all security vulnerabilities fixes can be
					backported
					to End-of-Life versions of PHP since they may require architectural changes in PHP itself. You are
					<strong>strongly</strong> advised to upgrade your server to a <a
							href="https://www.php.net/supported-versions.php">supported PHP version</a> as soon as possible.
				</p>

			<?php elseif ($isSecurity): ?>
				<h3>Security reminder</h3>

				<p>
					Your version of PHP, <?php echo $longVersion ?>, has entered the “Security Support” phase of its life on
					<?php echo $securityDateFormatted ?>. As such, only security issues will be addressed but not
					any of its known functional issues (“bugs”). Unfixed functional issues in PHP can lead to your site not
					working
					properly. It is advisable to plan migrating your site to a
					<a href="https://www.php.net/supported-versions.php">supported PHP version</a> no later than
					<?php echo $eolDateFormatted ?> – that's when PHP
					<?php echo $shortVersion ?> will become End-of-Life, therefore
					completely
					unsuitable for use on a live server.
				</p>
			<?php endif; ?>

			<?php if ($isSecurity || $isCurrent): ?>
				<h3>Why is my PHP version not supported?</h3>

				<p>
					Even though PHP <?php echo $shortVersion ?> will be supported by the PHP
					project until <?php echo $eolDateFormatted ?> we are unfortunately unable to provide
					support for it in our software. This has to do either with missing features or third party libraries.
					Older
					PHP versions are missing features we require for our software to work efficiently and be written in a
					way
					that makes it possible for us to provide a plethora of relevant features while maintaining good quality
					control. Moreover, third party libraries we use to provide some of the software's features do not
					support
					older PHP versions for the same reason – so even if we don't absolutely need to use at least PHP
					<?php echo $minPHPVersion ?> the third party libraries do, making it impossible for our software to run on
					your
					older version <?php echo $shortVersion ?>. We apologize for the inconvenience.
				</p>
				<p>
					We'd like to remind you, however, that newer PHP versions are always faster and more well-tested than
					their
					predecessors. Upgrading your site to a newer PHP version will not only let our software run but will
					also
					make your site faster, more stable and help it perform better in search engine results.
				</p>
			<?php endif; ?>
		</div>

		<?php return false;
	}
}

/**
 * Immediately executes the akeeba_common_wrongphp() function on all of our software except Kickstart.
 */
if (!defined('KICKSTART'))
{
	try
	{
		return akeeba_common_wrongphp(array(
			// Configuration -- Override before calling this script
			'minPHPVersion'         => isset($minPHPVersion) ? $minPHPVersion : '7.2.0',
			'recommendedPHPVersion' => isset($recommendedPHPVersion) ? $recommendedPHPVersion : '7.3',
			'softwareName'          => isset($softwareName) ? $softwareName : 'This software',
			'silentResults'         => isset($silentResults) ? $silentResults : false,
			// Override these to test the script
			'longVersion'           => isset($longVersion) ? $longVersion : PHP_VERSION,
			'shortVersion'          => isset($shortVersion) ? $shortVersion : sprintf('%d.%d', PHP_MAJOR_VERSION, PHP_MINOR_VERSION),
			'currentTimestamp'      => isset($currentTimestamp) ? $currentTimestamp : time(),
		));
	}
	catch (Exception $e)
	{
		// This should never happen
		return false;
	}
}DatabaseFilters/Html.php000060400000007364152457233650011240 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\DatabaseFilters;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\DatabaseFilters;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\HTML\HTMLHelper as JHtml;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Uri\Uri as JUri;

/**
 * View for database table exclusion
 */
class Html extends BaseView
{
	use ProfileIdAndName;

	/**
	 * SELECT element for choosing a database root
	 *
	 * @var  string
	 */
	public $root_select = '';

	/**
	 * List of database roots
	 *
	 * @var  array
	 */
	public $roots = [];

	/**
	 * Main page
	 */
	public function onBeforeMain()
	{
		// Load Javascript files
		$this->container->template->addJS('media://com_akeeba/js/FileFilters.min.js', true, false, $this->container->mediaVersion);
		$this->container->template->addJS('media://com_akeeba/js/DatabaseFilters.min.js', true, false, $this->container->mediaVersion);

		/** @var DatabaseFilters $model */
		$model = $this->getModel();

		// Add custom submenus
		$task    = $model->getState('browse_task', 'normal', 'cmd');
		$toolbar = $this->container->toolbar;

		$toolbar->appendLink(
			JText::_('COM_AKEEBA_FILEFILTERS_LABEL_NORMALVIEW'),
			JUri::base() . 'index.php?option=com_akeeba&view=DatabaseFilters&task=normal',
			($task == 'normal')
		);
		$toolbar->appendLink(
			JText::_('COM_AKEEBA_FILEFILTERS_LABEL_TABULARVIEW'),
			JUri::base() . 'index.php?option=com_akeeba&view=DatabaseFilters&task=tabular',
			($task == 'tabular')
		);

		// Get a JSON representation of the available roots
		$root_info = $model->get_roots();
		$roots     = [];
		$options   = [];

		if (!empty($root_info))
		{
			// Loop all dir definitions
			foreach ($root_info as $def)
			{
				$roots[]   = $def->value;
				$options[] = JHtml::_('select.option', $def->value, $def->text);
			}
		}

		$siteRoot          = '[SITEDB]';
		$selectOptions     = [
			'list.select' => $siteRoot,
			'id'          => 'active_root',
		];
		$this->root_select = JHtml::_('select.genericlist', $options, 'root', $selectOptions);
		$this->roots       = $roots;
		$platform          = $this->container->platform;

		// Add script options
		$platform->addScriptOptions('akeeba.System.params.AjaxURL', 'index.php?option=com_akeeba&view=DatabaseFilters&task=ajax');

		switch ($task)
		{
			case 'normal':
			default:
				$this->setLayout('default');

				// Get the database entities GUI data
				$platform->addScriptOptions('akeeba.DatabaseFilters.guiData', $model->make_listing($siteRoot));
				$platform->addScriptOptions('akeeba.DatabaseFilters.viewType', 'list');

				break;

			case 'tabular':
				$this->setLayout('tabular');

				// Get the filter data for tabular display
				$platform->addScriptOptions('akeeba.DatabaseFilters.guiData', $model->get_filters($siteRoot));
				$platform->addScriptOptions('akeeba.DatabaseFilters.viewType', 'tabular');


				break;
		}

		// Translations
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIROOT');
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIERRORFILTER');
		JText::script('COM_AKEEBA_DBFILTER_TYPE_TABLES');
		JText::script('COM_AKEEBA_DBFILTER_TYPE_TABLEDATA');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_MISC');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_TABLE');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_VIEW');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_PROCEDURE');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_FUNCTION');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_TRIGGER');
		JText::script('COM_AKEEBA_DBFILTER_TABLE_META_ROWCOUNT');

		$this->getProfileIdAndName();
	}
}
hhvm.php000060400000002441152457233650006230 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

// Protect from unauthorized access
defined('_JEXEC') or die();

?>

<div style="margin: 1em">
	<h1>We have detected that you are running HHVM instead of PHP. This software WILL NOT WORK properly on HHVM. Please switch to PHP 7 instead.</h1>
	<hr/>
	<p>
        HHVM was Facebook's attempt at modernizing the PHP 5.x language and making it faster. Unfortunately it's also incompatible with PHP proper.
        PHP 7 has solved all these issues. It's fast, modern and <em>fully compatible with our software</em>.
        Please switch to PHP 7. If you are unsure how to do that, contact your host or the person responsible for maintaining your server.
        They are the only people who can help you configure your server.
	</p>
	<p>
        Kindly note that HHVM is not -and has never been- a supported execution environment for our software.
        As a result, if you see this message on your site you are unfortunately ineligible for support and / or filing bug reports.
        Please switch to PHP 7. If your problem persists after that we can help you / accept your bug report. Thank you for your understanding.
	</p>
</div>
ViewTraits/ProfileList.php000060400000002500152457233650011617 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\ViewTraits;

// Protect from unauthorized access
use Joomla\CMS\HTML\HTMLHelper;

defined('_JEXEC') || die();

trait ProfileList
{
	/**
	 * List of backup profiles, for use with JHtmlSelect
	 *
	 * @var   array
	 */
	public $profileList = [];

	/**
	 * Populates the profileList property with an options list for use by JHtmlSelect
	 *
	 * @param   bool  $includeId  Should I include the profile ID in front of the name?
	 *
	 * @return  void
	 */
	protected function getProfileList($includeId = true)
	{
		/** @var \JDatabaseDriver $db */
		$db = $this->container->db;

		$query = $db->getQuery(true)
			->select([
				$db->qn('id'),
				$db->qn('description'),
			])->from($db->qn('#__ak_profiles'))
			->order($db->qn('id') . " ASC");

		$db->setQuery($query);
		$rawList = $db->loadAssocList();

		$this->profileList = [];

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

		foreach ($rawList as $row)
		{
			$description = $row['description'];

			if ($includeId)
			{
				$description = '#' . $row['id'] . '. ' . $description;
			}

			$this->profileList[] = HTMLHelper::_('select.option', $row['id'], $description);
		}
	}
}
ViewTraits/ProfileIdAndName.php000060400000002567152457233650012501 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\ViewTraits;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\Profiles;
use Akeeba\Engine\Platform;

trait ProfileIdAndName
{
	/**
	 * Active profile ID
	 *
	 * @var  int
	 */
	public $profileId = 0;

	/**
	 * Active profile's description
	 *
	 * @var  string
	 */
	public $profileName = '';

	/**
	 * Is this profile available as an One Click Backup icon? 0/1
	 *
	 * @var  int
	 */
	public $quickIcon = 0;

	/**
	 * Find the currently active profile ID and name and put them in properties accessible by the view template
	 */
	protected function getProfileIdAndName()
	{
		/** @var Profiles $profilesModel */
		$profilesModel = $this->container->factory->model('Profiles')->tmpInstance();
		$profileId     = Platform::getInstance()->get_active_profile();

		try
		{
			$this->profileName = $profilesModel->findOrFail($profileId)->description;
			$this->profileId   = $profileId;
			$this->quickIcon   = $profilesModel->quickicon;
		}
		catch (\Exception $e)
		{
			$this->container->platform->setSessionVar('profile', 1, 'akeeba');

			$this->profileId   = 1;
			$this->profileName = $profilesModel->findOrFail(1)->description;
		}
	}
}
ConfigurationWizard/Html.php000060400000003250152457233650012161 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\ConfigurationWizard;

// Protect from unauthorized access
defined('_JEXEC') || die();

use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\Language\Text;

class Html extends BaseView
{
	protected function onBeforeMain()
	{
		// Push translations
		// -- Wizard
		Text::script('COM_AKEEBA_CONFWIZ_UI_MINEXECTRY');
		Text::script('COM_AKEEBA_CONFWIZ_UI_CANTSAVEMINEXEC');
		Text::script('COM_AKEEBA_CONFWIZ_UI_SAVEMINEXEC');
		Text::script('COM_AKEEBA_CONFWIZ_UI_CANTDETERMINEMINEXEC');
		Text::script('COM_AKEEBA_CONFWIZ_UI_CANTFIXDIRECTORIES');
		Text::script('COM_AKEEBA_CONFWIZ_UI_CANTDBOPT');
		Text::script('COM_AKEEBA_CONFWIZ_UI_EXECTOOLOW');
		Text::script('COM_AKEEBA_CONFWIZ_UI_SAVINGMAXEXEC');
		Text::script('COM_AKEEBA_CONFWIZ_UI_CANTSAVEMAXEXEC');
		Text::script('COM_AKEEBA_CONFWIZ_UI_CANTDETERMINEPARTSIZE');
		Text::script('COM_AKEEBA_CONFWIZ_UI_PARTSIZE');

		// -- Backup
		Text::script('COM_AKEEBA_BACKUP_TEXT_LASTRESPONSE', true);

		// Load the Configuration Wizard Javascript file
		$this->container->template->addJS('media://com_akeeba/js/Backup.min.js', true, false, $this->container->mediaVersion);
		$this->container->template->addJS('media://com_akeeba/js/ConfigurationWizard.min.js', true, false, $this->container->mediaVersion);

		$platform = $this->container->platform;
		$platform->addScriptOptions('akeeba.System.params.AjaxURL', 'index.php?option=com_akeeba&view=ConfigurationWizard&task=ajax');

		// Set the layour
		$this->setLayout('wizard');
	}
}
Log/Html.php000060400000005043152457233650006714 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Log;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\Log;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use Akeeba\Engine\Factory;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\HTML\HTMLHelper;

/**
 * View controller for the Log Viewer page
 */
class Html extends BaseView
{
	use ProfileIdAndName;

	/**
	 * Big log file threshold: 2Mb
	 */
	public const bigLogSize = 2097152;
	/**
	 * JHtml list of available log files
	 *
	 * @var  array
	 */
	public $logs = [];
	/**
	 * Currently selected log file tag
	 *
	 * @var  string
	 */
	public $tag;
	/**
	 * Is the select log too big for being
	 *
	 * @var bool
	 */
	public $logTooBig = false;
	/**
	 * Size of the log file
	 *
	 * @var int
	 */
	public $logSize = 0;

	/**
	 * The main page of the log viewer. It allows you to select a profile to display. When you do it displays the IFRAME
	 * with the actual log content and the button to download the raw log file.
	 *
	 * @return  void
	 */
	public function onBeforeMain()
	{
		// Load the view-specific Javascript
		$this->container->template->addJS('media://com_akeeba/js/Log.min.js', true, false, $this->container->mediaVersion);

		if (version_compare(JVERSION, '3.999.999', 'lt'))
		{
			HTMLHelper::_('formbehavior.chosen');
		}

		// Get a list of log names
		/** @var Log $model */
		$model      = $this->getModel();
		$this->logs = $model->getLogList();

		$tag = $model->getState('tag', '', 'string');

		if (empty($tag))
		{
			$tag = null;
		}

		$this->tag = $tag;

		// Let's check if the file is too big to display
		if ($this->tag)
		{
			$logFile = Factory::getLog()->getLogFilename($this->tag);

			if (!@is_file($logFile) && @file_exists(substr($logFile, 0, -4)))
			{
				/**
				 * Transitional period: the log file akeeba.tag.log.php may not exist but the akeeba.tag.log does. This
				 * addresses this transition.
				 */
				$logFile = substr($logFile, 0, -4);
			}

			if (@file_exists($logFile))
			{
				$this->logSize   = filesize($logFile);
				$this->logTooBig = ($this->logSize >= self::bigLogSize);
			}
		}

		if ($this->logTooBig)
		{
			$src = 'index.php?option=com_akeeba&view=Log&task=inlineRaw&&tag=' . urlencode($this->tag) . '&tmpl=component';
			$this->container->platform->addScriptOptions('akeeba.Log.iFrameSrc', $src);
		}

		$this->getProfileIdAndName();
	}
}
Log/Raw.php000060400000001564152457233650006545 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Log;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\Log;
use Akeeba\Engine\Platform;
use FOF40\View\DataView\Html as BaseView;

/**
 * View controller for the Log Viewer page
 */
class Raw extends BaseView
{
	/**
	 * Currently selected log file tag
	 *
	 * @var  string
	 */
	public $tag;

	/**
	 * Renders the actual log content, for use in the IFRAME
	 *
	 * @return  void
	 */
	public function onBeforeIframe()
	{
		/** @var Log $model */
		$model = $this->getModel();
		$tag   = $model->getState('tag', '', 'string');

		if (empty($tag))
		{
			$tag = null;
		}

		$this->tag = $tag;

		$this->setLayout('raw');
	}
}
Manage/Html.php000060400000035147152457233650007373 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Manage;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Model\Profiles;
use Akeeba\Backup\Admin\Model\Statistics;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use DateTimeZone;
use Exception;
use FOF40\Date\Date;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\Factory as JFactory;
use Joomla\CMS\HTML\HTMLHelper as JHtml;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Pagination\Pagination;
use Joomla\CMS\Uri\Uri as JUri;
use stdClass;

/**
 * View controller for the Backup Now page
 */
class Html extends BaseView
{
	/**
	 * Should I use the user's local time zone for display?
	 *
	 * @var  boolean
	 */
	public $useLocalTime;

	/**
	 * Time format string to use for the time zone suffix
	 *
	 * @var  string
	 */
	public $timeZoneFormat;

	/**
	 * The backup record for the showcomment view
	 *
	 * @var  array
	 */
	public $record = [];

	/**
	 * The backup record ID for the showcomment view
	 *
	 * @var  int
	 */
	public $record_id = 0;

	/**
	 * List of Profiles objects
	 *
	 * @var  array
	 */
	public $profiles = [];

	/**
	 * List of profiles for JHtmlSelect
	 *
	 * @var  array
	 */
	public $profilesList = [];

	/**
	 * List of frozen options for JHtmlSelect
	 *
	 * @var  array
	 */
	public $frozenList = [];

	/**
	 * Order direction, ASC/DESC
	 *
	 * @var  string
	 */
	public $order_Dir = 'DESC';

	/**
	 * Description filter
	 *
	 * @var string
	 */
	public $fltDescription = '';

	/**
	 * From date filter
	 *
	 * @var  string
	 */
	public $fltFrom = '';

	/**
	 * To date filter
	 *
	 * @var  string
	 */
	public $fltTo = '';

	/**
	 * Origin filter
	 *
	 * @var  string
	 */
	public $fltOrigin = '';

	/**
	 * Profile filter
	 *
	 * @var  string
	 */
	public $fltProfile = '';

	/**
	 * Frozen records filter
	 *
	 * @var string
	 */
	public $fltFrozen = '';

	/**
	 * List of records to display
	 *
	 * @var  array
	 */
	public $items = [];

	/**
	 * Pagination object
	 *
	 * @var Pagination
	 */
	public $pagination = null;

	/**
	 * Date format for the backup start time
	 *
	 * @var  string
	 */
	public $dateFormat = '';

	/**
	 * Should I pormpt the user ot run the configuration wizard?
	 *
	 * @var  bool
	 */
	public $promptForBackupRestoration = false;

	/**
	 * Sorting order options
	 *
	 * @var  array
	 */
	public $sortFields = [];

	/**
	 * Cache the user permissions
	 *
	 * @var   array
	 *
	 * @since 5.3.0
	 */
	public $permissions = [];

	/**
	 * List the backup records
	 *
	 * @return  void
	 *
	 * @throws  Exception
	 */
	public function onBeforeMain()
	{
		// Load custom Javascript for this page
		$this->container->template->addJS('media://com_akeeba/js/Manage.min.js', true, false, $this->container->mediaVersion);

		$user              = $this->container->platform->getUser();
		$this->permissions = [
			'configure' => $user->authorise('akeeba.configure', 'com_akeeba'),
			'backup'    => $user->authorise('akeeba.backup', 'com_akeeba'),
			'download'  => $user->authorise('akeeba.download', 'com_akeeba'),
		];


		/** @var Profiles $profilesModel */
		$profilesModel           = $this->container->factory->model('Profiles')->tmpInstance();
		$enginesPerPprofile      = $profilesModel->getPostProcessingEnginePerProfile();
		$this->enginesPerProfile = $enginesPerPprofile;

		// "Show warning first" download button.
		JText::script('COM_AKEEBA_BUADMIN_LOG_DOWNLOAD_CONFIRM', false);
		$this->container->platform->addScriptOptions('akeeba.Manage.baseURI', JUri::base());

		if (version_compare(JVERSION, '3.999.999', 'le'))
		{
			JHtml::_('behavior.calendar');
		}

		$hash = 'akeebamanage';

		// ...ordering
		$platform = $this->container->platform;
		$input    = $this->input;

		// ...filter state
		$this->fltDescription   = $platform->getUserStateFromRequest($hash . 'filter_description', 'description', $input, '');
		$this->fltFrom          = $platform->getUserStateFromRequest($hash . 'filter_from', 'from', $input, '');
		$this->fltTo            = $platform->getUserStateFromRequest($hash . 'filter_to', 'to', $input, '');
		$this->fltOrigin        = $platform->getUserStateFromRequest($hash . 'filter_origin', 'origin', $input, '');
		$this->fltProfile       = $platform->getUserStateFromRequest($hash . 'filter_profile', 'profile', $input, '');
		$this->fltFrozen        = $platform->getUserStateFromRequest($hash . 'filter_frozen', 'frozen', $input, '');

		$this->lists            = new stdClass();
		$this->lists->order     = $platform->getUserStateFromRequest($hash . 'filter_order', 'filter_order', $input, 'backupstart');
		$this->lists->order_Dir = $platform->getUserStateFromRequest($hash . 'filter_order_Dir', 'filter_order_Dir', $input, 'DESC');

		$filters  = $this->getFilters();
		$ordering = $this->getOrdering();

		/** @var Statistics $model */
		$model       = $this->getModel();
		$this->items = $model->getStatisticsListWithMeta(false, $filters, $ordering);

		// Default limits
		$defaultLimit = 20;

		if (!$this->container->platform->isCli() && class_exists('JFactory'))
		{
			$app = JFactory::getApplication();

			if (method_exists($app, 'get'))
			{
				$defaultLimit = $app->get('list_limit');
			}
		}

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

		// Let's create an array indexed with the profile id for better handling
		$profiles = $profilesModel->get(true);

		$profilesList = [
			JHtml::_('select.option', '', '–' . JText::_('COM_AKEEBA_BUADMIN_LABEL_PROFILEID') . '–'),
		];

		if (!empty($profiles))
		{
			foreach ($profiles as $profile)
			{
				$profilesList[] = JHtml::_('select.option', $profile->id, '#' . $profile->id . '. ' . $profile->description);
			}
		}

		// Assign data to the view
		$this->profiles     = $profiles; // Profiles
		$this->profilesList = $profilesList; // Profiles list for select box
		$this->itemCount    = count($this->items);
		$this->pagination   = $model->getPagination($filters); // Pagination object

		$this->frozenList = [
			JHtml::_('select.option', '', '–' . JText::_('COM_AKEEBA_BUADMIN_LABEL_FROZEN_SELECT') . '–'),
			JHtml::_('select.option', '1', JText::_('COM_AKEEBA_BUADMIN_LABEL_FROZEN_FROZEN')),
			JHtml::_('select.option', '2', JText::_('COM_AKEEBA_BUADMIN_LABEL_FROZEN_UNFROZEN')),
		];

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

		// Date format
		$dateFormat       = $this->container->params->get('dateformat', '');
		$dateFormat       = trim($dateFormat);
		$this->dateFormat = !empty($dateFormat) ? $dateFormat : JText::_('DATE_FORMAT_LC4');

		// Time zone options
		$this->useLocalTime   = $this->container->params->get('localtime', '1') == 1;
		$this->timeZoneFormat = $this->container->params->get('timezonetext', 'T');

		// Should I show the prompt for the configuration wizard?
		$this->promptForBackupRestoration = $this->container->params->get('show_howtorestoremodal', 1) != 0;

		// Construct the array of sorting fields
		$this->sortFields = [
			'id'          => JText::_('COM_AKEEBA_BUADMIN_LABEL_ID'),
			'description' => JText::_('COM_AKEEBA_BUADMIN_LABEL_DESCRIPTION'),
			'backupstart' => JText::_('COM_AKEEBA_BUADMIN_LABEL_START'),
			'profile_id'  => JText::_('COM_AKEEBA_BUADMIN_LABEL_PROFILEID'),
		];
	}

	/**
	 * Edit a backup record's description and comment
	 *
	 * @return  void
	 */
	public function onBeforeShowcomment()
	{
		/** @var Statistics $model */
		$model           = $this->getModel();
		$id              = $model->getState('id', 0, 'int');
		$record          = Platform::getInstance()->get_statistics($id);
		$this->record    = $record;
		$this->record_id = $id;

		$this->setLayout('comment');
	}

	/**
	 * File size formatting function. COnverts number of bytes to a human readable represenation.
	 *
	 * @param   int     $sizeInBytes         Size in bytes
	 * @param   int     $decimals            How many decimals should I use? Default: 2
	 * @param   string  $decSeparator        Decimal separator
	 * @param   string  $thousandsSeparator  Thousands grouping character
	 *
	 * @return string
	 */
	public function formatFilesize($sizeInBytes, $decimals = 2, $decSeparator = '.', $thousandsSeparator = '')
	{
		if ($sizeInBytes <= 0)
		{
			return '-';
		}

		$units = ['b', 'KB', 'MB', 'GB', 'TB'];
		$unit  = floor(log($sizeInBytes, 2) / 10);

		if ($unit == 0)
		{
			$decimals = 0;
		}

		return number_format($sizeInBytes / (1024 ** $unit), $decimals, $decSeparator, $thousandsSeparator) . ' ' . $units[$unit];
	}

	/**
	 * Translates the internal backup type (e.g. cli) to a human readable string
	 *
	 * @param   string  $recordType  The internal backup type
	 *
	 * @return  string
	 */
	public function translateBackupType($recordType)
	{
		static $backup_types = null;

		if (!is_array($backup_types))
		{
			// Load a mapping of backup types to textual representation
			$scripting    = Factory::getEngineParamsProvider()->loadScripting();
			$backup_types = [];
			foreach ($scripting['scripts'] as $key => $data)
			{
				$backup_types[$key] = JText::_($data['text']);
			}
		}

		if (array_key_exists($recordType, $backup_types))
		{
			return $backup_types[$recordType];
		}

		return '&ndash;';
	}

	/**
	 * Returns the origin's translated name and the appropriate icon class
	 *
	 * @param   array  $record  A backup record
	 *
	 * @return  array  array(originTranslation, iconClass)
	 */
	protected function getOriginInformation($record)
	{
		$originLanguageKey = 'COM_AKEEBA_BUADMIN_LABEL_ORIGIN_' . $record['origin'];
		$originDescription = JText::_($originLanguageKey);

		switch (strtolower($record['origin']))
		{
			case 'backend':
				$originIcon = 'akion-android-desktop';
				break;

			case 'frontend':
				$originIcon = 'akion-ios-world';
				break;

			case 'json':
				$originIcon = 'akion-android-cloud';
				break;

			case 'cli':
				$originIcon = 'akion-ios-paper-outline';
				break;

			case 'xmlrpc':
				$originIcon = 'akion-code';
				break;

			case 'lazy':
				$originIcon = 'akion-cube';
				break;

			default:
				$originIcon = 'akion-help';
				break;
		}

		if (empty($originLanguageKey) || ($originDescription == $originLanguageKey))
		{
			$originDescription = '&ndash;';
			$originIcon        = 'akion-help';

			return [$originDescription, $originIcon];
		}

		return [$originDescription, $originIcon];
	}

	/**
	 * Get the start time and duration of a backup record
	 *
	 * @param   array  $record  A backup record
	 *
	 * @return  array  array(startTimeAsString, durationAsString)
	 */
	protected function getTimeInformation($record)
	{
		$utcTimeZone = new DateTimeZone('UTC');
		$startTime   = new Date($record['backupstart'], $utcTimeZone);
		$endTime     = new Date($record['backupend'], $utcTimeZone);

		$duration = $endTime->toUnix() - $startTime->toUnix();

		if ($duration > 0)
		{
			$seconds  = $duration % 60;
			$duration = $duration - $seconds;

			$minutes  = ($duration % 3600) / 60;
			$duration = $duration - $minutes * 60;

			$hours    = $duration / 3600;
			$duration = sprintf('%02d', $hours) . ':' . sprintf('%02d', $minutes) . ':' . sprintf('%02d', $seconds);
		}
		else
		{
			$duration = '';
		}

		$user   = $this->container->platform->getUser();
		$userTZ = $user->getParam('timezone', 'UTC');
		$tz     = new DateTimeZone($userTZ);
		$startTime->setTimezone($tz);

		$timeZoneSuffix = '';

		if (!empty($this->timeZoneFormat))
		{
			$timeZoneSuffix = $startTime->format($this->timeZoneFormat, $this->useLocalTime);
		}

		return [
			$startTime->format($this->dateFormat, $this->useLocalTime),
			$duration,
			$timeZoneSuffix,
		];
	}

	/**
	 * Get the class and icon for the backup status indicator
	 *
	 * @param   array  $record  A backup record
	 *
	 * @return  array  array(class, icon)
	 */
	protected function getStatusInformation($record)
	{
		$statusClass = '';

		switch ($record['meta'])
		{
			case 'ok':
				$statusIcon  = 'akion-checkmark';
				$statusClass = 'akeeba-label--green';
				break;
			case 'pending':
				$statusIcon  = 'akion-play';
				$statusClass = 'akeeba-label--orange';
				break;
			case 'fail':
				$statusIcon  = 'akion-android-cancel';
				$statusClass = 'akeeba-label--red';
				break;
			case 'remote':
				$statusIcon  = 'akion-cloud';
				$statusClass = 'akeeba-label--teal';
				break;
			default:
				$statusIcon  = 'akion-trash-a';
				$statusClass = 'akeeba-label--grey';
				break;
		}

		return [$statusClass, $statusIcon];
	}

	/**
	 * Get the profile name for the backup record (or "–" if the profile no longer exists)
	 *
	 * @param   array  $record  A backup record
	 *
	 * @return  string
	 */
	protected function getProfileName($record)
	{
		$profileName = '&mdash;';

		if (isset($this->profiles[$record['profile_id']]))
		{
			$profileName = $this->escape($this->profiles[$record['profile_id']]->description);

			return $profileName;
		}

		return $profileName;
	}

	/**
	 * Get the filters in a format that Akeeba Engine understands
	 *
	 * @return  array
	 */
	private function getFilters()
	{
		$filters = [];

		if ($this->fltDescription)
		{
			$filters[] = [
				'field'   => 'description',
				'operand' => 'LIKE',
				'value'   => $this->fltDescription,
			];
		}

		if ($this->fltFrom && $this->fltTo)
		{
			$filters[] = [
				'field'   => 'backupstart',
				'operand' => 'BETWEEN',
				'value'   => $this->fltFrom,
				'value2'  => $this->fltTo,
			];
		}
		elseif ($this->fltFrom)
		{
			$filters[] = [
				'field'   => 'backupstart',
				'operand' => '>=',
				'value'   => $this->fltFrom,
			];
		}
		elseif ($this->fltTo)
		{
			$toDate = new Date($this->fltTo);
			$to     = $toDate->format('Y-m-d') . ' 23:59:59';

			$filters[] = [
				'field'   => 'backupstart',
				'operand' => '<=',
				'value'   => $to,
			];
		}

		if ($this->fltOrigin)
		{
			$filters[] = [
				'field'   => 'origin',
				'operand' => '=',
				'value'   => $this->fltOrigin,
			];
		}

		if ($this->fltProfile)
		{
			$filters[] = [
				'field'   => 'profile_id',
				'operand' => '=',
				'value'   => (int) $this->fltProfile,
			];
		}

		if ($this->fltFrozen == 1)
		{
			$filters[] = [
				'field'   => 'frozen',
				'operand' => '=',
				'value'   => 1,
			];
		}
		elseif ($this->fltFrozen == 2)
		{
			$filters[] = [
				'field'   => 'frozen',
				'operand' => '=',
				'value'   => 0,
			];
		}

		if (empty($filters))
		{
			$filters = null;
		}

		return $filters;
	}

	/**
	 * Get the list ordering in a format that Akeeba Engine understands
	 *
	 * @return  array
	 */
	private function getOrdering()
	{
		$order = [
			'by'    => $this->lists->order,
			'order' => strtoupper($this->lists->order_Dir),
		];

		return $order;
	}

}
Backup/Html.php000060400000014514152457233650007403 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Backup;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\Helper\Status;
use Akeeba\Backup\Admin\Helper\Utils;
use Akeeba\Backup\Admin\Model\Backup;
use Akeeba\Backup\Admin\Model\ControlPanel;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileList;
use Akeeba\Engine\Factory;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\Language\Text;

/**
 * View controller for the Backup Now page
 */
class Html extends BaseView
{
	use ProfileList, ProfileIdAndName;

	/**
	 * Do we have errors preventing the backup from starting?
	 *
	 * @var  bool
	 */
	public $hasErrors = false;

	/**
	 * Do we have warnings which may affect –but do not prevent– the backup from running?
	 *
	 * @var  bool
	 */
	public $hasWarnings = false;

	/**
	 * The HTML of the warnings cell
	 *
	 * @var  string
	 */
	public $warningsCell = '';

	/**
	 * Backup description
	 *
	 * @var  string
	 */
	public $description = '';

	/**
	 * Default backup description
	 *
	 * @var  string
	 */
	public $defaultDescription = '';

	/**
	 * Backup comment
	 *
	 * @var  string
	 */
	public $comment = '';

	/**
	 * JSON string of the backup domain name to titles associative array
	 *
	 * @var  array
	 */
	public $domains = '';

	/**
	 * Maximum execution time in seconds
	 *
	 * @var  int
	 */
	public $maxExecutionTime = 10;

	/**
	 * Execution time bias, in percentage points (0-100)
	 *
	 * @var  int
	 */
	public $runtimeBias = 75;

	/**
	 * URL to return to after the backup is complete
	 *
	 * @var  string
	 */
	public $returnURL = '';

	/**
	 * Is the output directory unwritable?
	 *
	 * @var  bool
	 */
	public $unwriteableOutput = false;

	/**
	 * has the user configured an ANGIE password?
	 *
	 * @var  string
	 */
	public $hasANGIEPassword = '';

	/**
	 * Should I autostart the backup?
	 *
	 * @var  string
	 */
	public $autoStart = false;

	/**
	 * Should I display desktop notifications? 0/1
	 *
	 * @var  int
	 */
	public $desktopNotifications = 0;

	/**
	 * Should I try to automatically resume the backup in case of an error? 0/1
	 *
	 * @var  int
	 */
	public $autoResume = 0;

	/**
	 * After how many seconds should I try to automatically resume the backup?
	 *
	 * @var  int
	 */
	public $autoResumeTimeout = 10;

	/**
	 * How many times in total should I try to automatically resume the backup?
	 *
	 * @var  int
	 */
	public $autoResumeRetries = 3;

	/**
	 * Should I prompt the user to run the Configuration Wizard?
	 *
	 * @var  bool
	 */
	public $promptForConfigurationWizard = false;

	/**
	 * Runs before displaying the backup page
	 */
	public function onBeforeMain()
	{
		// Load the view-specific Javascript
		$this->container->template->addJS('media://com_akeeba/js/Backup.min.js', true, false, $this->container->mediaVersion);

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

		/** @var ControlPanel $cpanelmodel */
		$cpanelmodel = $this->container->factory->model('ControlPanel')->tmpInstance();

		// Load the Status Helper
		$helper = Status::getInstance();

		// Determine default description
		$default_description = $this->getDefaultDescription();

		// Load data from the model state
		$backup_description = $model->getState('description', $default_description, 'string');
		$comment            = $model->getState('comment', '', 'html');
		$returnurl          = Utils::safeDecodeReturnUrl($model->getState('returnurl', ''));

		// Get the maximum execution time and bias
		$engineConfiguration = Factory::getConfiguration();
		$maxexec             = $engineConfiguration->get('akeeba.tuning.max_exec_time', 14) * 1000;
		$bias                = $engineConfiguration->get('akeeba.tuning.run_time_bias', 75);

		// Check if the output directory is writable
		$warnings         = Factory::getConfigurationChecks()->getDetailedStatus();
		$unwritableOutput = array_key_exists('001', $warnings);

		// Pass on data
		$this->getProfileList();
		$this->getProfileIdAndName();

		$this->hasErrors                    = !$helper->status;
		$this->hasWarnings                  = $helper->hasQuirks();
		$this->warningsCell                 = $helper->getQuirksCell(!$helper->status);
		$this->description                  = $backup_description;
		$this->defaultDescription           = $default_description;
		$this->comment                      = $comment;
		$this->domains                      = $this->getDomains();
		$this->maxExecutionTime             = $maxexec;
		$this->runtimeBias                  = $bias;
		$this->returnURL                    = $returnurl;
		$this->unwriteableOutput            = $unwritableOutput;
		$this->autoStart                    = $model->getState('autostart', 0, 'boolean');
		$this->desktopNotifications         = $this->container->params->get('desktop_notifications', '0') ? 1 : 0;
		$this->autoResume                   = $engineConfiguration->get('akeeba.advanced.autoresume', 1);
		$this->autoResumeTimeout            = $engineConfiguration->get('akeeba.advanced.autoresume_timeout', 10);
		$this->autoResumeRetries            = $engineConfiguration->get('akeeba.advanced.autoresume_maxretries', 3);
		$this->promptForConfigurationWizard = $engineConfiguration->get('akeeba.flag.confwiz', 0) == 0;
		$this->hasANGIEPassword = !empty(trim($engineConfiguration->get('engine.installer.angie.key', '')));
	}

	/**
	 * Get the default description for this backup attempt
	 *
	 * @return  string
	 */
	private function getDefaultDescription()
	{
		return $this->getModel()->getDefaultDescription();
	}

	/**
	 * Get a list of backup domain keys and titles
	 *
	 * @return  array
	 */
	private function getDomains()
	{
		$engineConfiguration = Factory::getConfiguration();
		$script              = $engineConfiguration->get('akeeba.basic.backup_type', 'full');
		$scripting           = Factory::getEngineParamsProvider()->loadScripting();
		$domains             = [];

		if (empty($scripting))
		{
			return $domains;
		}

		foreach ($scripting['scripts'][$script]['chain'] as $domain)
		{
			$description = Text::_($scripting['domains'][$domain]['text']);
			$domain_key  = $scripting['domains'][$domain]['domain'];
			$domains[]   = [$domain_key, $description];
		}

		return $domains;
	}
}
Configuration/Html.php000060400000007237152457233650011011 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Configuration;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\Language\Text as JText;

class Html extends BaseView
{
	use ProfileIdAndName;

	/**
	 * Status of the settings encryption: -1 disabled by user, 0 not available, 1 enabled and active
	 *
	 * @var  int
	 */
	public $secureSettings = 0;

	/**
	 * Should I show the Configuration Wizard popup prompt?
	 *
	 * @var  bool
	 */
	public $promptForConfigurationWizard = false;

	/**
	 * Executes when displaying the page
	 */
	public function onBeforeMain()
	{
		$this->container->template->addJS('media://com_akeeba/js/Configuration.min.js', true, false, $this->container->mediaVersion);

		$this->getProfileIdAndName();

		// Are the settings secured?
		$this->secureSettings = $this->getSecureSettingsOption();

		// Should I show the Configuration Wizard popup prompt?
		$this->promptForConfigurationWizard = Factory::getConfiguration()->get('akeeba.flag.confwiz', 0) != 1;

		// Push script options
		$urls = array(
			'browser'      => addslashes('index.php?option=com_akeeba&view=Browser&processfolder=1&tmpl=component&folder='),
			'ftpBrowser'   => addslashes('index.php?option=com_akeeba&view=FTPBrowser'),
			'sftpBrowser'  => addslashes('index.php?option=com_akeeba&view=SFTPBrowser'),
			'testFtp'      => addslashes('index.php?option=com_akeeba&view=Configuration&task=testftp'),
			'testSftp'     => addslashes('index.php?option=com_akeeba&view=Configuration&task=testsftp'),
			'dpeauthopen'  => addslashes('index.php?option=com_akeeba&view=Configuration&task=dpeoauthopen&format=raw'),
			'dpecustomapi' => addslashes('index.php?option=com_akeeba&view=Configuration&task=dpecustomapi&format=raw'),
		);

		// Push script options
		$platform = $this->container->platform;
		$platform->addScriptOptions('akeeba.Configuration.URLs', $urls);
		$platform->addScriptOptions('akeeba.Configuration.GUIData', json_decode(Factory::getEngineParamsProvider()->getJsonGuiDefinition(), true));


		// Push translations
		JText::script('COM_AKEEBA_CONFIG_UI_BROWSE');
		JText::script('COM_AKEEBA_CONFIG_UI_CONFIG');
		JText::script('COM_AKEEBA_CONFIG_UI_REFRESH');
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIROOT');
		JText::script('COM_AKEEBA_CONFIG_UI_FTPBROWSER_TITLE');
		JText::script('COM_AKEEBA_CONFIG_DIRECTFTP_TEST_OK');
		JText::script('COM_AKEEBA_CONFIG_DIRECTFTP_TEST_FAIL');
		JText::script('COM_AKEEBA_CONFIG_DIRECTSFTP_TEST_OK');
		JText::script('COM_AKEEBA_CONFIG_DIRECTSFTP_TEST_FAIL');
	}

	/**
	 * Returns the support status of settings encryption. The possible values are:
	 * -1 Disabled by the user
	 *  0 Enabled by inactive (not supported by the server)
	 *  1 Enabled and active
	 *
	 * @return  int
	 */
	private function getSecureSettingsOption()
	{
		// Encryption is disabled by the user
		if (Platform::getInstance()->get_platform_configuration_option('useencryption', -1) == 0)
		{
			return -1;
		}

		// Encryption is not supported by this server
		if (!Factory::getSecureSettings()->supportsEncryption())
		{
			return 0;
		}

		$filename = JPATH_COMPONENT_ADMINISTRATOR . '/BackupEngine/serverkey.php';

		// Encryption enabled, supported and a key file is present: encryption enabled
		if (is_file($filename))
		{
			return 1;
		}

		// Encryption enabled, supported but and a key file is NOT present: encryption not available
		return 0;
	}
}
.htaccess000060400000000246152457233650006354 0ustar00<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
<IfModule mod_authz_core.c>
  <RequireAll>
    Require all denied
  </RequireAll>
</IfModule>
fef.php000060400000003071152457233650006026 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

// Protect from unauthorized access
defined('_JEXEC') or die();

?>

<div style="margin: 1em">
	<h1>Akeeba Frontend Framework (FEF) could not be found on this site</h1>
	<hr/>
	<div class="alert alert-warning">
		<h2>
			This component requires the Akeeba Frontend Framework (FEF) to be installed on your site. Please go to <a
					href="https://www.akeeba.com/download/official/fef.html">our download page</a> to download it, then install it on your site.
		</h2>
	</div>
	<hr/>
	<h4>Further information</h4>
	<p>
        FEF is the name of our custom CSS framework. It's responsible for rendering the interface of our Joomla!
		extensions. It is automatically installed when you install our extensions on your site.
	</p>
	<p>
		FEF can be missing from your site either because Joomla failed to install it or because you, another Super User,
		or another extension mistakenly uninstalled it.
	</p>
	<p>
		If it's missing we cannot display the interface to this component. That's why you see this message.
	</p>
	<p>
		You do not have to worry about adding bloat to your site. FEF is very small. It will also be automatically
		uninstalled when you uninstall all components which depend on it.
	</p>
	<p>
		FEF is installed in the <code>media/fef</code> folder under your site's root. It appears in Joomla's Extensions,
		Manage page as <code>file_fef</code>. Please do not remove it from your site.
	</p>
</div>
web.config000060400000001025152457233650006516 0ustar00<?xml version="1.0"?>
<!--
    This only works on IIS 7 or later. See https://www.iis.net/configreference/system.webserver/security/requestfiltering/fileextensions
-->
<configuration>
    <system.webServer>
        <security>
            <requestFiltering>
                <fileExtensions allowUnlisted="false" >
                    <clear />
                    <add fileExtension=".html" allowed="true"/>
                </fileExtensions>
            </requestFiltering>
        </security>
    </system.webServer>
</configuration>Profiles/Html.php000060400000002434152457233650007757 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Profiles;

// Protect from unauthorized access
defined('_JEXEC') || die();

use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use FOF40\View\DataView\Html as BaseView;
use Joomla\CMS\HTML\HTMLHelper as JHtml;
use Joomla\CMS\Language\Text as JText;

/**
 * View controller for the profiles management page
 */
class Html extends BaseView
{
	use ProfileIdAndName;

	/**
	 * Sorting order fields
	 *
	 * @var  array
	 */
	public $sortFields;

	/**
	 * The default layout, shows a list of profiles
	 */
	function onBeforeBrowse()
	{
		$this->getProfileIdAndName();

		// Get Sort By fields
		$this->sortFields = [
			'id'          => JText::_('JGRID_HEADING_ID'),
			'description' => JText::_('COM_AKEEBA_PROFILES_COLLABEL_DESCRIPTION'),
		];

		parent::onBeforeBrowse();

		JHtml::_('behavior.multiselect');
		JHtml::_('dropdown.init');
	}

	/**
	 * The edit layout, editing a profile's name
	 */
	protected function onBeforeEdit()
	{
		parent::onBeforeEdit();

		// Include tooltip support
		if (version_compare(JVERSION, '3.999.999', 'lt'))
		{
			JHtml::_('behavior.tooltip');
		}
	}
}
Profiles/Json.php000060400000000556152457233650007767 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\Profiles;

// Protect from unauthorized access
defined('_JEXEC') || die();

use FOF40\View\DataView\Json as BaseView;

class Json extends BaseView
{

}
RegExFileFilter/Html.php000060400000005341152457233650011154 0ustar00<?php
/**
 * @package   akeebabackup
 * @copyright Copyright (c)2006-2023 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

namespace Akeeba\Backup\Admin\View\RegExFileFilter;

// Protect from unauthorized access
use Akeeba\Backup\Admin\Model\RegExFileFilters;
use Akeeba\Backup\Admin\View\ViewTraits\ProfileIdAndName;
use Akeeba\Engine\Factory;
use Joomla\CMS\HTML\HTMLHelper as JHtml;
use Joomla\CMS\Language\Text as JText;
use Joomla\CMS\Uri\Uri as JUri;

defined('_JEXEC') || die();

class Html extends \FOF40\View\DataView\Html
{
	use ProfileIdAndName;

	/**
	 * SELECT element for choosing a database root
	 *
	 * @var  string
	 */
	public $root_select = '';

	/**
	 * List of database roots
	 *
	 * @var  array
	 */
	public $roots = [];

	/**
	 * Main page
	 */
	public function onBeforeMain()
	{
		// Load Javascript files
		$this->container->template->addJS('media://com_akeeba/js/FileFilters.min.js', true, false, $this->container->mediaVersion);
		$this->container->template->addJS('media://com_akeeba/js/RegExFileFilter.min.js', true, false, $this->container->mediaVersion);

		/** @var RegExFileFilters $model */
		$model = $this->getModel();

		// Get a JSON representation of the available roots
		$filters   = Factory::getFilters();
		$root_info = $filters->getInclusions('dir');
		$roots     = array();
		$options   = array();

		if (!empty($root_info))
		{
			// Loop all dir definitions
			foreach ($root_info as $dir_definition)
			{
				if (is_null($dir_definition[1]))
				{
					// Site root definition has a null element 1. It is always pushed on top of the stack.
					array_unshift($roots, $dir_definition[0]);
				}
				else
				{
					$roots[] = $dir_definition[0];
				}

				$options[] = JHtml::_('select.option', $dir_definition[0], $dir_definition[0]);
			}
		}
		$site_root         = $roots[0];
		$this->root_select = JHtml::_('select.genericlist', $options, 'root', [
			'list.select' => $site_root,
			'id'          => 'active_root',
		]);
		$this->roots       = $roots;

		// Pass script options
		$platform = $this->container->platform;
		$platform->addScriptOptions('akeeba.System.params.AjaxURL', JUri::base() . 'index.php?option=com_akeeba&view=RegExFileFilters&task=ajax');
		$platform->addScriptOptions('akeeba.RegExFileFilter.guiData', $model->get_regex_filters($site_root));

		$this->getProfileIdAndName();

		// Push translations
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIROOT');
		JText::script('COM_AKEEBA_FILEFILTERS_LABEL_UIERRORFILTER');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_DIRECTORIES');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPFILES');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_SKIPDIRS');
		JText::script('COM_AKEEBA_FILEFILTERS_TYPE_FILES');

	}
}