shell bypass 403
<?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']; } }