Файловый менеджер - Редактировать - /home/wuectly/www/03cbe/Controller.zip
Назад
PK �X!]�e�/6{ 6{ Controller.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Controller\Exception\CannotGetName; use FOF30\Controller\Exception\TaskNotFound; use FOF30\Model\Model; use FOF30\View\View; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\Controller\ViewController; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; /** * Class Controller * * A generic MVC controller implementation * * @property-read \FOF30\Input\Input $input The input object (magic __get returns the Input from the Container) */ class Controller { /** * Instance container. * * @var Controller */ protected static $instance; /** * The name of the controller * * @var array */ protected $name = null; /** * The mapped task that was performed. * * @var string */ protected $doTask; /** * Bit mask to enable routing through JRoute on redirects. The value can be: * * 0 = never * 1 = frontend only * 2 = backend only * 3 = always * * @var int */ protected $autoRouting = 0; /** * Should I protect against state bleedover? When this is enabled the default model's state hash will be * automatically set to include the controller name i.e. `com_example.controllerName.modelName.` instead of * `com_example.modelName.`. This will happen ONLY if the preventStateBleedover flag is set, the controller and * model names are different and the model doesn't set its own hash (or override getHash altogether). * * You should only need to enable this feature when you have multiple controllers using the _same_ Model as their * default. For example, if you have a blog component with Latest and Posts Controllers, both using the Posts Model * as their default Model the state variables set in the latest posts page would bleed over to the posts page. This * can include filtering and pagination preferences, resulting in a confusing experience for the user. * * Caveat: if you are using a different Controller class for singular / plural view names you will need to override * getModel() yourself. Otherwise the state of the singular view would be disjointed from the state of the * plural view (since the Controller names are different). That's the reason why this feature is turned off * by default. * * False = same behavior as FOF 3.0.0 to 3.1.1 inclusive. * * @var bool */ protected $preventStateBleedover = false; /** * Redirect message. * * @var string */ protected $message; /** * Redirect message type. * * @var string */ protected $messageType; /** * Array of class methods * * @var array */ protected $methods; /** * The set of search directories for resources (views). * * @var array */ protected $paths; /** * URL for redirection. * * @var string */ protected $redirect; /** * Current or most recently performed task. * * @var string */ protected $task; /** * Array of class methods to call for a given task. * * @var array */ protected $taskMap; /** * The current view name; you can override it in the configuration * * @var string */ protected $view = ''; /** * The current layout; you can override it in the configuration * * @var string */ protected $layout = null; /** * A cached copy of the class configuration parameter passed during initialisation * * @var array */ protected $config = []; /** * Overrides the name of the view's default model * * @var string */ protected $modelName = null; /** * Overrides the name of the view's default view * * @var string */ protected $viewName = null; /** * An array of Model instances known to this Controller * * @var array[Model] */ protected $modelInstances = []; /** * An array of View instances known to this Controller * * @var array[View] */ protected $viewInstances = []; /** * The container attached to this Controller * * @var Container */ protected $container = null; /** * The tasks for which caching should be enabled by default * * @var array */ protected $cacheableTasks = []; /** * How user group membership affects caching. The values are: * - 0 : Not taken into account, everyone sees the same page, always * - 1 : Only user groups are taken into account (default behaviour of FOF 3.0 to 3.4.2) * - 2 : The user ID itself is taken into account * * @var bool * @since 3.4.3 */ protected $userCaching = 1; /** * An associative array for required ACL privileges per task. For example: * array( * 'edit' => 'core.edit', * 'jump' => 'foobar.jump', * 'alwaysallow' => 'true', * 'neverallow' => 'false' * ); * * You can use the notation '@task' which means 'apply the same privileges as "task"'. If you create a reference * back to yourself (e.g. 'mytask' => array('@mytask')) it will return TRUE. * * @var array */ protected $taskPrivileges = []; /** * Enable CSRF protection on selected tasks. The possible values are: * * 0 Disabled; no token checks are performed * 1 Enabled; token checks are always performed * 2 Only on HTML requests and backend; token checks are always performed in the back-end and in the front-end * only when format is 'html' * 3 Only on back-end; token checks are performed only in the back-end * * @var integer */ protected $csrfProtection = 2; /** * Public constructor of the Controller class. You can pass the following variables in the $config array: * name string The name of the Controller. Default: auto detect from the class name * default_task string The task to use when none is specified. Default: main * autoRouting int See the autoRouting property * csrfProtection int See the csrfProtection property * viewName string The view name. Default: the same as the controller name * modelName string The model name. Default: the same as the controller name * viewConfig array The configuration overrides for the View. * modelConfig array The configuration overrides for the Model. * * @param Container $container The application container * @param array $config The configuration array * * @return Controller */ public function __construct(Container $container, array $config = []) { // Initialise $this->methods = []; $this->message = null; $this->messageType = 'message'; $this->paths = []; $this->redirect = null; $this->taskMap = []; // Get a local copy of the container $this->container = $container; // Determine the methods to exclude from the base class. $xMethods = get_class_methods('\\FOF30\\Controller\\Controller'); // Get the public methods in this class using reflection. $r = new \ReflectionClass($this); $rMethods = $r->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($rMethods as $rMethod) { $mName = $rMethod->getName(); // If the developer screwed up and declared one of the helper method public do NOT make them available as // tasks. if ((substr($mName, 0, 8) == 'onBefore') || (substr($mName, 0, 7) == 'onAfter') || substr($mName, 0, 1) == '_') { continue; } // Add default display method if not explicitly declared. if (!in_array($mName, $xMethods) || $mName == 'display' || $mName == 'main') { $this->methods[] = $mName; // Auto register the methods as tasks. $this->taskMap[$mName] = $mName; } } if (isset($config['name'])) { $this->name = $config['name']; } // Get the default values for the component and view names $this->view = $this->getName(); $this->layout = $this->input->getCmd('layout', null); // If the default task is set, register it as such if (array_key_exists('default_task', $config) && !empty($config['default_task'])) { $this->registerDefaultTask($config['default_task']); } else { $this->registerDefaultTask('main'); } // Cache the config $this->config = $config; // Set any model/view name overrides if (array_key_exists('viewName', $config) && !empty($config['viewName'])) { $this->setViewName($config['viewName']); } if (array_key_exists('modelName', $config) && !empty($config['modelName'])) { $this->setModelName($config['modelName']); } // Apply the autoRouting preference if (array_key_exists('autoRouting', $config)) { $this->autoRouting = (int) $config['autoRouting']; } // Apply the csrfProtection preference if (array_key_exists('csrfProtection', $config)) { $this->csrfProtection = (int) $config['csrfProtection']; } // Apply the preventStateBleedover preference if (array_key_exists('preventStateBleedover', $config)) { $this->preventStateBleedover = (bool) ((int) $config['preventStateBleedover']); } } /** * 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; } /** * Executes a given controller task. The onBefore<task> and onAfter<task> * methods are called automatically if they exist. * * @param string $task The task to execute, e.g. "browse" * * @return null|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute($task) { $this->task = $task; if (!isset($this->taskMap[$task]) && !isset($this->taskMap['__default'])) { throw new TaskNotFound(Text::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404); } $result = $this->triggerEvent('onBeforeExecute', [&$task]); if ($result === false) { return false; } $eventName = 'onBefore' . ucfirst($task); $result = $this->triggerEvent($eventName); if ($result === false) { return false; } // Do not allow the display task to be directly called if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { $doTask = null; } // Record the actual task being fired $this->doTask = $doTask; $ret = $this->$doTask(); $eventName = 'onAfter' . ucfirst($task); $result = $this->triggerEvent($eventName); if ($result === false) { return false; } $result = $this->triggerEvent('onAfterExecute', [$task]); if ($result === false) { return false; } return $ret; } /** * Default task. Assigns a model to the view and asks the view to render * itself. * * YOU MUST NOT USE THIS TASK DIRECTLY IN A URL. It is supposed to be * used ONLY inside your code. In the URL, use task=browse instead. * * @param bool $cachable Is this view cacheable? * @param bool $urlparams Add your safe URL parameters (see further down in the code) * @param string $tpl The name of the template file to parse * * @return void */ public function display($cachable = false, $urlparams = false, $tpl = null) { $document = $this->container->platform->getDocument(); if ($document instanceof Document) { $viewType = $document->getType(); } else { $viewType = $this->input->getCmd('format', 'html'); } $view = $this->getView(); $view->setTask($this->task); $view->setDoTask($this->doTask); // Get/Create the model if ($model = $this->getModel()) { // Push the model into the view (as default) $view->setDefaultModel($model); } // Set the layout if (!is_null($this->layout)) { $view->setLayout($this->layout); } $conf = $this->container->platform->getConfig(); if ($cachable && ($viewType != 'feed') && ($conf->get('caching') >= 1)) { // Get a JCache object $option = $this->input->get('option', 'com_foobar', 'cmd'); // Set up a cache ID based on component, view, task and user group assignment $user = $this->container->platform->getUser(); if ($user->guest) { $groups = []; } else { $groups = $user->groups; } $userId = $user->guest ? 0 : $user->id; switch ($this->userCaching) { case 0: // Developer chose to apply the same caching to everyone $groups = []; $userId = 0; break; case 1: // Developer chose to apply caching per user group membership only $userId = 0; break; } $importantParameters = []; // Set up safe URL parameters if (!is_array($urlparams)) { $urlparams = [ 'option' => 'CMD', 'view' => 'CMD', 'task' => 'CMD', 'format' => 'CMD', 'layout' => 'CMD', 'id' => 'INT', ]; } if (is_array($urlparams)) { /** @var CMSApplication $app */ $app = Factory::getApplication(); $registeredurlparams = null; if (!empty($app->registeredurlparams)) { $registeredurlparams = $app->registeredurlparams; } else { $registeredurlparams = new \stdClass; } foreach ($urlparams as $key => $value) { // Add your safe url parameters with variable type as value {@see JFilterInput::clean()}. $registeredurlparams->$key = $value; // Add the URL-important parameters into the array $importantParameters[$key] = $this->input->get($key, null, $value); } $app->registeredurlparams = $registeredurlparams; } // Create the cache ID after setting the registered URL params, as they are used to generate the ID $cacheId = md5(serialize([ Cache::makeId(), $view->getName(), $this->doTask, $groups, $userId, $importantParameters, ])); // Get the cached view or cache the current view try { /** @var ViewController $cache */ $cache = Factory::getCache($option, 'view'); $cache->get($view, 'display', $cacheId); } catch (CacheExceptionInterface $e) { // Display without caching $view->display($tpl); } } else { // Display without caching $view->display($tpl); } } /** * Alias to the display() task * * @codeCoverageIgnore */ public function main() { $this->display(); } /** * 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 * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return Model The instance of the Model known to this Controller */ public function getModel($name = null, $config = []) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->modelName)) { $modelName = $this->modelName; } else { $modelName = $this->view; } if (!array_key_exists($modelName, $this->modelInstances)) { if (empty($config) && isset($this->config['modelConfig'])) { $config = $this->config['modelConfig']; } if (empty($name)) { $config['modelTemporaryInstance'] = true; $controllerName = $this->getName(); if ($controllerName != $modelName) { $config['hash_view'] = $controllerName; } } else { // Other classes are loaded with persistent state disabled and their state/input blanked out $config['modelTemporaryInstance'] = false; $config['modelClearState'] = true; $config['modelClearInput'] = true; } $this->modelInstances[$modelName] = $this->container->factory->model(ucfirst($modelName), $config); } return $this->modelInstances[$modelName]; } /** * Returns a named View 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 * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return View The instance of the Model known to this Controller */ public function getView($name = null, $config = []) { if (!empty($name)) { $viewName = $name; } elseif (!empty($this->viewName)) { $viewName = $this->viewName; } else { $viewName = $this->view; } if (!array_key_exists($viewName, $this->viewInstances)) { if (empty($config) && isset($this->config['viewConfig'])) { $config = $this->config['viewConfig']; } $viewType = $this->input->getCmd('format', 'html'); // Get the model's class name $this->viewInstances[$viewName] = $this->container->factory->view($viewName, $viewType, $config); } return $this->viewInstances[$viewName]; } /** * Pushes a named view to the Controller * * @param string $viewName The name of the View * @param View $view The actual View object to push * * @return void */ public function setView($viewName, View &$view) { $this->viewInstances[$viewName] = $view; } /** * Set the name of the view to be used by this Controller * * @param string $viewName The name of the view * * @return void */ public function setViewName($viewName) { $this->viewName = $viewName; } /** * Set the name of the model to be used by this Controller * * @param string $modelName The name of the model * * @return void */ public function setModelName($modelName) { $this->modelName = $modelName; } /** * Pushes a named model to the Controller * * @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; } /** * Method to get the controller name * * The controller name is set 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 controller * * @throws CannotGetName If it's impossible to determine the name and it's not set */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\Controller\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName(Text::_('LIB_FOF_CONTROLLER_ERR_GET_NAME'), 500); } $this->name = $r[2]; } return $this->name; } /** * Get the last task that is being performed or was most recently performed. * * @return string The task that is being performed or was most recently performed. */ public function getTask() { return $this->task; } /** * Gets the available tasks in the controller. * * @return array Array[i] of task names. */ public function getTasks() { return $this->methods; } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. */ public function redirect() { if ($this->redirect) { $this->container->platform->redirect($this->redirect, 301, $this->message, $this->messageType); return true; } return false; } /** * Register the default task to perform if a mapping is not found. * * @param string $method The name of the method in the derived class to perform if a named task is not found. * * @return Controller This object to support chaining. */ public function registerDefaultTask($method) { $this->registerTask('__default', $method); return $this; } /** * Register (map) a task to a method in the class. * * @param string $task The task. * @param string $method The name of the method in the derived class to perform for this task. * * @return Controller This object to support chaining. */ public function registerTask($task, $method) { if (in_array($method, $this->methods)) { $this->taskMap[$task] = $method; } return $this; } /** * Unregister (unmap) a task in the class. * * @param string $task The task. * * @return Controller This object to support chaining. */ public function unregisterTask($task) { unset($this->taskMap[$task]); return $this; } /** * Sets the internal message that is passed with a redirect * * @param string $text Message to display on redirect. * @param string $type Message type. Optional, defaults to 'message'. * * @return string Previous message */ public function setMessage($text, $type = 'message') { $previous = $this->message; $this->message = $text; $this->messageType = $type; return $previous; } /** * Set a URL for browser redirection. * * @param string $url URL to redirect to. * @param string $msg Message to display on redirect. Optional, defaults to value set internally by * controller, if any. * @param string $type Message type. Optional, defaults to 'message' or the type set by a previous call to * setMessage. * * @return Controller This object to support chaining. */ public function setRedirect($url, $msg = null, $type = null) { // If we're parsing a non-SEF URL decide whether to use JRoute or not if (strpos($url, 'index.php') === 0) { $isAdmin = $this->container->platform->isBackend(); $auto = false; if (($this->autoRouting == 2 || $this->autoRouting == 3) && $isAdmin) { $auto = true; } if (($this->autoRouting == 1 || $this->autoRouting == 3) && !$isAdmin) { $auto = true; } if ($auto) { $url = Route::_($url, false); } /** * Joomla 4 does not add the base URI to redirections. * * This means that all bare redirects, e.g. to 'index.php?option=com_example', no longer work correctly. * * In the frontend, if your site is located in a subdirectory e.g. /foobar you get redirected to * /index.php?option=com_example instead of /foobar/index.php?option=com_example * * In the backend, you're redirected to /index.php?option=com_example instead of the expected * /administrator/index.php?option=com_example which breaks your application since the backend redirects to * the frontend. * * This is an undocumented b/c break in Joomla 4. It even breaks some of the core components... * * The following code detects bare redirect URLs and adds the base URI path if auto-routing has been * disabled, automatically fixing the observed issue. It only does that on Joomla 4 since adding the base * URI on Joomla 3 can cause redirection problems. */ if (!$auto && version_compare(JVERSION, '3.999.999', 'gt')) { $url = Uri::base() . $url; } } // Set the redirection $this->redirect = $url; if ($msg !== null) { // Controller may have set this directly $this->message = $msg; } // Ensure the type is not overwritten by a previous call to setMessage. if (empty($this->messageType)) { $this->messageType = 'message'; } // If the type is explicitly set, set it. if (!empty($type)) { $this->messageType = $type; } return $this; } /** * Returns true if there is a redirect set in the controller * * @return boolean */ public function hasRedirect() { return !empty($this->redirect); } /** * Provides CSRF protection through the forced use of a secure token. If the token doesn't match the one in the * session we return false. * * @return bool * * @throws \Exception */ protected function csrfProtection() { static $isCli = null, $isAdmin = null; $platform = $this->container->platform; if (is_null($isCli)) { $isCli = $platform->isCli(); $isAdmin = $platform->isBackend(); } switch ($this->csrfProtection) { // Never case 0: return true; break; // Always case 1: break; // Only back-end and HTML format case 2: if ($isCli) { return true; } elseif (!$isAdmin && ($this->input->get('format', 'html', 'cmd') != 'html')) { return true; } break; // Only back-end case 3: if (!$isAdmin) { return true; } break; } // Check for a session token $token = $this->container->platform->getToken(false); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $hasToken = $this->input->get('_token', null, 'none') == $token; } if ($hasToken) { $view = $this->input->getCmd('view'); $task = $this->input->getCmd('task'); Log::add( "FOF: You are using a legacy session token in (view, task)=($view, $task). Support for legacy tokens will go away. Use form tokens instead.", Log::WARNING, 'deprecated' ); } // Check for a form token if (!$hasToken) { $token = $this->container->platform->getToken(true); $hasToken = $this->input->get($token, false, 'none') == 1; if (!$hasToken) { $view = $this->input->getCmd('view'); $task = $this->input->getCmd('task'); Log::add( "FOF: You are using the insecure _token form variable in (view, task)=($view, $task). Support for it will go away. Submit a variable with the token as the name and a value of 1 instead.", Log::WARNING, 'deprecated' ); $hasToken = $this->input->get('_token', null, 'none') == $token; } } if (!$hasToken) { $platform->raiseError(403, Text::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN')); return false; } return true; } /** * 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. $this->checkACL('@something') if there is no onBeforeSomething and the event starts with onBefore * 3. Joomla! plugin event onComFoobarControllerItemBeforeSomething($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)) { switch (count($arguments)) { case 0: $result = $this->{$event}(); break; case 1: $result = $this->{$event}($arguments[0]); break; case 2: $result = $this->{$event}($arguments[0], $arguments[1]); break; case 3: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2]); break; case 4: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3]); break; case 5: $result = $this->{$event}($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]); break; default: $result = call_user_func_array([$this, $event], $arguments); break; } } // If there is no handler method perform a simple ACL check elseif (substr($event, 0, 8) == 'onBefore') { $task = substr($event, 8); $result = $this->checkACL('@' . $task); } 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) . 'Controller'; $prefix .= ucfirst($this->getName()); // The event name will be something like onComFoobarItemsBeforeSomething $event = $prefix . $event; // Call the Joomla! plugins $results = $this->container->platform->runPlugins($event, $arguments); if (!empty($results)) { foreach ($results as $result) { if ($result === false) { return false; } } } return true; } /** * Checks if the current user has enough privileges for the requested ACL area. * * @param string $area The ACL area, e.g. core.manage. * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL($area) { $area = $this->getACLRuleFor($area); if (is_bool($area)) { return $area; } if (in_array(strtolower($area), ['false', '0', 'no', '403'])) { return false; } if (in_array(strtolower($area), ['true', '1', 'yes'])) { return true; } if (in_array(strtolower($area), ['guest'])) { return $this->container->platform->getUser()->guest; } if (in_array(strtolower($area), ['user'])) { return !$this->container->platform->getUser()->guest; } if (empty($area)) { return true; } return $this->container->platform->authorise($area, $this->container->componentName); } /** * Resolves @task and &callback notations for ACL privileges * * @param string $area The task notation to resolve * @param array $oldAreas Areas we've already been redirected from, used to detect circular references * * @return mixed The resolved ACL privilege */ protected function getACLRuleFor($area, $oldAreas = []) { // If it's a ¬ation return the callback result if (substr($area, 0, 1) == '&') { $oldAreas[] = $area; $method = substr($area, 1); // Method not found? Assume true. if (!method_exists($this, $method)) { return true; } $area = $this->$method(); return $this->getACLRuleFor($area, $oldAreas); } // If it's not an @notation return the raw string if (substr($area, 0, 1) != '@') { return $area; } // Get the array index (other task) $index = substr($area, 1); // If the referenced task has no ACL map, return true if (!isset($this->taskPrivileges[$index])) { $index = strtolower($index); if (!isset($this->taskPrivileges[$index])) { return true; } } // Get the new ACL area $newArea = $this->taskPrivileges[$index]; $oldAreas[] = $area; // Circular reference found if (in_array($newArea, $oldAreas)) { return true; } // We've found an ACL privilege. Return it. if (substr($area, 0, 1) != '@') { return $newArea; } // We have another reference. Resolve it. return $this->getACLRuleFor($newArea, $oldAreas); } } PK �X!]9k_9� � Mixin/PredefinedTaskList.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Mixin; defined('_JEXEC') || die; use FOF30\Controller\Controller; /** * Force a Controller to allow access to specific tasks only, no matter which tasks are already defined in this * Controller. * * Include this Trait and then in your constructor do this: * $this->setPredefinedTaskList(['atask', 'anothertask', 'something']); * * WARNING: If you override execute() you will need to copy the logic from this trait's execute() method. */ trait PredefinedTaskList { /** * A list of predefined tasks. Trying to access any other task will result in the first task of this list being * executed instead. * * @var array */ protected $predefinedTaskList = []; /** * Overrides the execute method to implement the predefined task list feature * * @param string $task The task to execute * * @return mixed The controller task result */ public function execute($task) { if (!in_array($task, $this->predefinedTaskList)) { $task = reset($this->predefinedTaskList); } return parent::execute($task); } /** * Sets the predefined task list and registers the first task in the list as the Controller's default task * * @param array $taskList The task list to register * * @return void */ public function setPredefinedTaskList(array $taskList) { /** @var Controller $this */ // First, unregister all known tasks which are not in the taskList $allTasks = $this->getTasks(); foreach ($allTasks as $task) { if (in_array($task, $taskList)) { continue; } $this->unregisterTask($task); } // Set the predefined task list $this->predefinedTaskList = $taskList; // Set the default task $this->registerDefaultTask(reset($this->predefinedTaskList)); } } PK �X!]�^�� � DataController.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller; defined('_JEXEC') || die; use FOF30\Container\Container; use FOF30\Controller\Exception\ItemNotFound; use FOF30\Controller\Exception\LockedRecord; use FOF30\Controller\Exception\NotADataModel; use FOF30\Controller\Exception\TaskNotFound; use FOF30\Model\DataModel; use FOF30\View\View; use Joomla\CMS\Language\Text; use Joomla\CMS\Response\JsonResponse; /** * Database-aware Controller * * @property-read \FOF30\Input\Input $input The input object (magic __get returns the Input from the Container) */ class DataController extends Controller { /** * Variables that should be taken in account while working with the cache. You can set them in Controller * constructor or inside onBefore* methods * * @var false|array */ protected $cacheParams = false; /** * An associative array for required ACL privileges per task. For example: * array( * 'edit' => 'core.edit', * 'jump' => 'foobar.jump', * 'alwaysallow' => 'true', * 'neverallow' => 'false' * ); * * You can use the notation '@task' which means 'apply the same privileges as "task"'. If you create a reference * back to yourself (e.g. 'mytask' => array('@mytask')) it will return TRUE. * * @var array */ protected $taskPrivileges = [ // Special privileges '*editown' => 'core.edit.own', // Privilege required to edit own record // Standard tasks 'add' => 'core.create', 'apply' => '&getACLForApplySave', // Apply task: call the getACLForApplySave method 'archive' => 'core.edit.state', 'cancel' => 'core.edit.state', 'copy' => '@add', // Maps copy ACLs to the add task 'edit' => 'core.edit', 'loadhistory' => '@edit', // Maps loadhistory ACLs to the edit task 'orderup' => 'core.edit.state', 'orderdown' => 'core.edit.state', 'publish' => 'core.edit.state', 'remove' => 'core.delete', 'forceRemove' => 'core.delete', 'save' => '&getACLForApplySave', // Save task: call the getACLForApplySave method 'savenew' => 'core.create', 'saveorder' => 'core.edit.state', 'trash' => 'core.edit.state', 'unpublish' => 'core.edit.state', ]; /** * An indexed array of default values for the add task. Since the add task resets the model you can't set these * values directly to the model. Instead, the defaultsForAdd values will be fed to model's bind() after it's reset * and before the session-stored item data is bound to the model object. * * @var array */ protected $defaultsForAdd = []; /** * Public constructor of the Controller class. You can pass the following variables in the $config array, * on top of what you already have in the base Controller class: * * taskPrivileges array ACL privileges for each task * cacheableTasks array The cache-enabled tasks * * @param Container $container The application container * @param array $config The configuration array */ public function __construct(Container $container, array $config = []) { parent::__construct($container, $config); // Set up a default model name if none is provided if (empty($this->modelName)) { $this->modelName = $container->inflector->pluralize($this->view); } // Set up a default view name if none is provided if (empty($this->viewName)) { $this->viewName = $container->inflector->pluralize($this->view); } if (isset($config['cacheableTasks'])) { if (!is_array($config['cacheableTasks'])) { $config['cacheableTasks'] = explode(',', $config['cacheableTasks']); $config['cacheableTasks'] = array_map('trim', $config['cacheableTasks']); } $this->cacheableTasks = $config['cacheableTasks']; } elseif ($this->container->platform->isBackend()) { $this->cacheableTasks = []; } else { $this->cacheableTasks = ['browse', 'read']; } if (isset($config['taskPrivileges']) && is_array($config['taskPrivileges'])) { $this->taskPrivileges = array_merge($this->taskPrivileges, $config['taskPrivileges']); } } /** * Executes a given controller task. The onBefore<task> and onAfter<task> methods are called automatically if they * exist. * * If $task == 'default' we will determine the CRUD task to use based on the view name and HTTP verb in the request, * overriding the routing. * * @param string $task The task to execute, e.g. "browse" * * @return null|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute($task) { if ($task == 'default') { $task = $this->getCrudTask(); } return parent::execute($task); } /** * Returns a named View 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 * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return View The instance of the Model known to this Controller */ public function getView($name = null, $config = []) { if (!empty($name)) { $viewName = $name; } elseif (!empty($this->viewName)) { $viewName = $this->viewName; } else { $viewName = $this->view; } if (!array_key_exists($viewName, $this->viewInstances)) { if (empty($config) && isset($this->config['viewConfig'])) { $config = $this->config['viewConfig']; } $viewType = $this->input->getCmd('format', 'html'); // Get the model's class name $this->viewInstances[$viewName] = $this->container->factory->view($viewName, $viewType, $config); } return $this->viewInstances[$viewName]; } /** * Implements a default browse task, i.e. read a bunch of records and send * them to the browser. * * @return void */ public function browse() { // Initialise the savestate $saveState = $this->input->get('savestate', -999, 'int'); if ($saveState == -999) { $saveState = true; } $this->getModel()->savestate($saveState); // Display the view $this->display(in_array('browse', $this->cacheableTasks), $this->cacheParams); } /** * Single record read. The id set in the request is passed to the model and * then the item layout is used to render the result. * * @return void * * @throws ItemNotFound When the item is not found */ public function read() { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); // If there is no record loaded, try loading a record based on the id passed in the input object if (!$model->getId()) { $ids = $this->getIDsFromRequest($model, true); if ($model->getId() != reset($ids)) { $key = strtoupper($this->container->componentName . '_ERR_' . $model->getName() . '_NOTFOUND'); throw new ItemNotFound(Text::_($key), 404); } } // Set the layout to item, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'item'; } elseif ($this->layout == 'default') { $this->layout = 'item'; } // Display the view $this->display(in_array('read', $this->cacheableTasks), $this->cacheParams); } /** * Single record add. The form layout is used to present a blank page. * * @return void */ public function add() { // Load and reset the model $model = $this->getModel()->savestate(false); $model->reset(); // Set the layout to form, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'form'; } elseif ($this->layout == 'default') { $this->layout = 'form'; } if (!empty($this->defaultsForAdd)) { $model->bind($this->defaultsForAdd); } // Get temporary data from the session, set if the save failed and we're redirected back here $sessionKey = $this->viewName . '.savedata'; $itemData = $this->container->platform->getSessionVar($sessionKey, null, $this->container->componentName); $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); if (!empty($itemData)) { $model->bind($itemData); } // Display the view $this->display(in_array('add', $this->cacheableTasks), $this->cacheParams); } /** * Single record edit. The ID set in the request is passed to the model, * then the form layout is used to edit the result. * * @return void */ public function edit() { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $userId = $this->container->platform->getUser()->id; try { if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->lock(); } catch (\Exception $e) { // Redirect on error if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); return; } // Set the layout to form, if it's not set in the URL if (empty($this->layout)) { $this->layout = 'form'; } elseif ($this->layout == 'default') { $this->layout = 'form'; } // Get temporary data from the session, set if the save failed and we're redirected back here $sessionKey = $this->viewName . '.savedata'; $itemData = $this->container->platform->getSessionVar($sessionKey, null, $this->container->componentName); $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); if (!empty($itemData)) { $model->bind($itemData); } // Display the view $this->display(in_array('edit', $this->cacheableTasks), $this->cacheParams); } /** * Save the incoming data and then return to the Edit task * * @return void */ public function apply() { // CSRF prevention $this->csrfProtection(); // Redirect to the edit task if (!$this->applySave()) { return; } $id = $this->input->get('id', 0, 'int'); $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Duplicates selected items * * @return void */ public function copy() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, true); $error = null; $copiedCount = 0; try { $status = true; foreach ($ids as $id) { $model->find($id); $model->copy(); $copiedCount++; } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); return; } if ($copiedCount > 0) { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_COPIED'); $this->setRedirect($url, Text::_($textKey)); return; } $this->setRedirect($url); } /** * Save the incoming data and then return to the Browse task * * @return void */ public function save() { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return; } $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Save the incoming data and then return to the Add task * * @return bool */ public function savenew() { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return; } $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_SAVED'); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->singularize($this->view) . '&task=add' . $this->getItemidURLSuffix(); $this->setRedirect($url, Text::_($textKey)); } /** * Save the incoming data as a copy of the given model and then redirect to the copied object edit view * * @return bool */ public function save2copy() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, true); $data = $this->input->getData(); unset($data[$model->getIdFieldName()]); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $model = $model->copy($data); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $model->getId() . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_COPIED'); $this->setRedirect($url, Text::_($textKey)); } } /** * Cancel the edit, check in the record and return to the Browse task * * @return void */ public function cancel() { $model = $this->getModel()->tmpInstance()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } if ($model->getId()) { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { try { $model->checkIn($userId); } catch (LockedRecord $e) { // Redirect to the display task if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); } } $model->unlock(); } // Remove any saved data $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); // Redirect to the display task if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url); } /** * Publish (set enabled = 1) an item. * * @return void */ public function publish() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = false; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->publish(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Unpublish (set enabled = 0) an item. * * @return void */ public function unpublish() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->unpublish(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Archive (set enabled = 2) an item. * * @return void */ public function archive() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->archive(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Trash (set enabled = -2) an item. * * @return void */ public function trash() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->trash(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Check in (unlock) items * * @return void */ public function checkin() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $model->checkIn(); } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Saves the order of the items * * @return void */ public function saveorder() { // CSRF prevention $this->csrfProtection(); $type = null; $msg = null; $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $orders = $this->input->get('order', [], 'array'); // Before saving the order, I have to check I the table really supports the ordering feature if (!$model->hasField('ordering')) { $msg = sprintf('%s does not support ordering.', $model->getTableName()); $type = 'error'; } else { $ordering = $model->getFieldAlias('ordering'); // Several methods could throw exceptions, so let's wrap everything in a try-catch try { if ($n = count($ids)) { for ($i = 0; $i < $n; $i++) { $item = $model->find($ids[$i]); $neworder = (int) $orders[$i]; if (!($item instanceof DataModel)) { continue; } if ($item->getId() == $ids[$i]) { $item->$ordering = $neworder; $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->save($item); } } } $model->reorder(); } catch (\Exception $e) { $msg = $e->getMessage(); $type = 'error'; } } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $msg, $type); } /** * Moves selected items one position down the ordering list * * @return void */ public function orderdown() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $error = null; try { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->move(1); $status = true; } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Moves selected items one position up the ordering list * * @return void */ public function orderup() { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $error = null; try { $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } $model->move(-1); $status = true; } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $this->setRedirect($url); } } /** * Delete or trash selected item(s). The model's softDelete flag determines if the items should be trashed (enabled * state changed to -2) or deleted (completely removed from database) * * @return void */ public function remove() { $this->deleteOrTrash(false); } /** * Deletes the selected item(s). Unlike remove() this method will force delete the record (completely removed from * database) * * @return void */ public function forceRemove() { $this->deleteOrTrash(true); } /** * Returns a named Model object. Makes sure that the Model is a database-aware model, throwing an exception * otherwise, when $name is null. * * @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 * @param array $config Configuration parameters to the Model. If skipped * we will use $this->config * * @return DataModel The instance of the Model known to this Controller * * @throws NotADataModel When the model type doesn't match our expectations */ public function getModel($name = null, $config = []) { $model = parent::getModel($name, $config); if (is_null($name) && !($model instanceof DataModel)) { throw new NotADataModel('Model ' . get_class($model) . ' is not a database-aware Model'); } return $model; } /** * Gets the list of IDs from the request data * * @param DataModel $model The model where the record will be loaded * @param bool $loadRecord When true, the record matching the *first* ID found will be loaded into $model * * @return array */ public function getIDsFromRequest(DataModel &$model, $loadRecord = true) { // Get the ID or list of IDs from the request or the configuration $cid = $this->input->get('cid', [], 'array'); $id = $this->input->getInt('id', 0); $kid = $this->input->getInt($model->getIdFieldName(), 0); $ids = []; if (is_array($cid) && !empty($cid)) { $ids = $cid; } else { if (empty($id)) { if (!empty($kid)) { $ids = [$kid]; } } else { $ids = [$id]; } } if ($loadRecord && !empty($ids)) { $id = reset($ids); $model->find(['id' => $id]); } return $ids; } /** * Method to load a row from version history * * @return boolean True if the content history is reverted, false otherwise * * @since 2.2 */ public function loadhistory() { $model = $this->getModel(); $model->lock(); $historyId = $this->input->get('version_id', null, 'integer'); $alias = $this->container->componentName . '.' . $this->view; $returnUrl = 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $returnUrl = $customURL; } try { $model->loadhistory($historyId, $alias); } catch (\Exception $e) { $this->setRedirect($returnUrl, $e->getMessage(), 'error'); $model->unlock(); return false; } // Access check. if (!$this->checkACL('@loadhistory')) { $this->setRedirect($returnUrl, Text::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'), 'error'); $model->unlock(); return false; } $model->store(); $this->setRedirect($returnUrl, Text::sprintf('JLIB_APPLICATION_SUCCESS_LOAD_HISTORY', $model->getState('save_date'), $model->getState('version_note'))); return true; } /** * Gets a URL suffix with the Itemid parameter. If it's not the front-end of the site, or if * there is no Itemid set it returns an empty string. * * @return string The &Itemid=123 URL suffix, or an empty string if Itemid is not applicable */ public function getItemidURLSuffix() { if ($this->container->platform->isFrontend() && ($this->input->getCmd('Itemid', 0) != 0)) { return '&Itemid=' . $this->input->getInt('Itemid', 0); } else { return ''; } } /** * Deal with JSON format: no redirects needed * * @param string $task The task being executed * * @return boolean True if everything went well */ protected function onAfterExecute($task) { // JSON shouldn't have redirects if ($this->hasRedirect() && $this->input->getCmd('format', 'html') == 'json') { // Error: deal with it in REST api way if ($this->messageType == 'error') { $response = new JsonResponse($this->message, $this->message, true); echo $response; $this->redirect = false; $this->container->platform->setHeader('Status', 500); return; } else { // Not an error, avoid redirect and display the record(s) $this->redirect = false; return $this->display(); } } return true; } /** * Determines the CRUD task to use based on the view name and HTTP verb used in the request. * * @return string The CRUD task (browse, read, edit, delete) */ protected function getCrudTask() { // By default, a plural view means 'browse' and a singular view means 'edit' $view = $this->input->getCmd('view', null); $task = $this->container->inflector->isPlural($view) ? 'browse' : 'edit'; // If the task is 'edit' but there's no logged in user switch to a 'read' task if (($task == 'edit') && !$this->container->platform->getUser()->id) { $task = 'read'; } // Check if there is an id passed in the request $id = $this->input->get('id', null, 'int'); if ($id == 0) { $ids = $this->input->get('ids', [], 'array'); if (!empty($ids)) { $id = array_shift($ids); } } // Get the request HTTP verb $requestMethod = 'GET'; if (isset($_SERVER['REQUEST_METHOD'])) { $requestMethod = strtoupper($_SERVER['REQUEST_METHOD']); } // Alter the task based on the verb switch ($requestMethod) { // POST and PUT result in a record being saved; no ID means creating a new record case 'POST': case 'PUT': $task = 'save'; break; // DELETE results in a record being deleted, as long as there is an ID case 'DELETE': if ($id) { $task = 'remove'; } break; // GET results in browse, edit or add depending on the ID case 'GET': default: // If it's an edit without an ID or ID=0, it's really an add if (($task == 'edit') && ($id == 0)) { $task = 'add'; } break; } return $task; } /** * Checks if the current user has enough privileges for the requested ACL area. This overridden method supports * asset tracking as well. * * @param string $area The ACL area, e.g. core.manage * * @return boolean True if the user has the ACL privilege specified */ protected function checkACL($area) { $area = $this->getACLRuleFor($area); $result = parent::checkACL($area); // Check if we're dealing with ids $ids = null; // First, check if there is an asset for this record /** @var DataModel $model */ $model = $this->getModel(); $ids = null; if (is_object($model) && ($model instanceof DataModel) && $model->isAssetsTracked()) { $ids = $this->getIDsFromRequest($model, false); } // No IDs tracked, return parent's result if (empty($ids)) { return $result; } // Asset tracking if (!is_array($ids)) { $ids = [$ids]; } $resource = $this->container->inflector->singularize($this->view); $isEditState = ($area == 'core.edit.state'); foreach ($ids as $id) { $asset = $this->container->componentName . '.' . $resource . '.' . $id; // Dedicated permission found, check it! $platform = $this->container->platform; if ($platform->authorise($area, $asset)) { return true; } // Fallback on edit.own, if not edit.state. First test if the permission is available. $editOwn = $this->getACLRuleFor('@*editown'); if ((!$isEditState) && ($platform->authorise($editOwn, $asset))) { $model->load($id); if (!$model->hasField('created_by')) { return false; } // Now test the owner is the user. $owner_id = (int) $model->getFieldValue('created_by', null); // If the owner matches 'me' then do the test. if ($owner_id == $platform->getUser()->id) { return true; } return false; } } // No result found? Not authorised. return false; } protected function deleteOrTrash($forceDelete = false) { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model, false); $error = null; try { $status = true; foreach ($ids as $id) { $model->find($id); $userId = $this->container->platform->getUser()->id; if ($model->isLocked($userId)) { $model->checkIn($userId); } if ($forceDelete) { $model->forceDelete(); } else { $model->delete(); } } } catch (\Exception $e) { $status = false; $error = $e->getMessage(); } // Redirect if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); if (!$status) { $this->setRedirect($url, $error, 'error'); } else { $textKey = strtoupper($this->container->componentName . '_LBL_' . $this->container->inflector->singularize($this->view) . '_DELETED'); $this->setRedirect($url, Text::_($textKey)); } } /** * Common method to handle apply and save tasks * * @return bool True on success */ protected function applySave() { // Load the model $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $userId = $this->container->platform->getUser()->id; $id = $model->getId(); $data = $this->input->getData(); if ($model->isLocked($userId)) { try { $model->checkIn($userId); } catch (LockedRecord $e) { // Redirect to the display task if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } $eventName = 'onAfterApplySaveError'; $result = $this->triggerEvent($eventName, [&$data, $id, $e]); $url = !empty($customURL) ? $customURL : 'index.php?option=' . $this->container->componentName . '&view=' . $this->container->inflector->pluralize($this->view) . $this->getItemidURLSuffix(); $this->setRedirect($url, $e->getMessage(), 'error'); return false; } } // Set the layout to form, if it's not set in the URL if (is_null($this->layout)) { $this->layout = 'form'; } // Save the data $status = true; $error = null; try { $eventName = 'onBeforeApplySave'; $result = $this->triggerEvent($eventName, [&$data]); if ($id != 0) { // Try to check-in the record if it's not a new one $model->unlock(); } // Save the data $model->save($data); $eventName = 'onAfterApplySave'; $result = $this->triggerEvent($eventName, [&$data, $model->getId()]); $this->input->set('id', $model->getId()); } catch (\Exception $e) { $status = false; $error = $e->getMessage(); $eventName = 'onAfterApplySaveError'; $result = $this->triggerEvent($eventName, [&$data, $model->getId(), $e]); } if (!$status) { // Cache the item data in the session. We may need to reuse them if the save fails. $itemData = $model->getData(); $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, $itemData, $this->container->componentName); // Redirect on error $id = $model->getId(); if ($customURL = $this->input->getBase64('returnurl', '')) { $customURL = base64_decode($customURL); } if (!empty($customURL)) { $url = $customURL; } elseif ($id != 0) { $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=edit&id=' . $id . $this->getItemidURLSuffix(); } else { $url = 'index.php?option=' . $this->container->componentName . '&view=' . $this->view . '&task=add' . $this->getItemidURLSuffix(); } $this->setRedirect($url, $error, 'error'); } else { $sessionKey = $this->viewName . '.savedata'; $this->container->platform->setSessionVar($sessionKey, null, $this->container->componentName); } return $status; } /** * Gets the applicable ACL privilege for the apply and save tasks. The value returned is: * - @add if the record's ID is empty / record doesn't exist * - True if the ACL privilege of the edit task (@edit) is allowed * - @editown if the owner of the record (field user_id, userid or user) is the same as the logged in user * - False if the record is not owned by the logged in user and the user doesn't have the @edit privilege * * @return bool|string */ protected function getACLForApplySave() { $model = $this->getModel(); if (!$model->getId()) { $this->getIDsFromRequest($model, true); } $id = $model->getId(); if (!$id) { return '@add'; } if ($this->checkACL('@edit')) { return true; } $user = $this->container->platform->getUser(); $uid = 0; if ($model->hasField('user_id')) { $uid = $model->getFieldValue('user_id'); } elseif ($model->hasField('userid')) { $uid = $model->getFieldValue('userid'); } elseif ($model->hasField('user')) { $uid = $model->getFieldValue('user'); } if (!empty($uid) && !$user->guest && ($user->id == $uid)) { return '@editown'; } return false; } } PK �X!]�]:�n n Exception/ItemNotFound.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when we can't find the requested item in a read task */ class ItemNotFound extends \RuntimeException { } PK �X!]�W` ` Exception/CannotGetName.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends \RuntimeException { } PK �X!]��_� � Exception/LockedRecord.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; /** * Exception thrown when the provided Model is locked for writing by another user */ class LockedRecord extends \RuntimeException { public function __construct($message = "", $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF_CONTROLLER_ERR_LOCKED'); } parent::__construct($message, $code, $previous); } } PK �X!] �#m m Exception/NotADataModel.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when the provided Model is not a DataModel */ class NotADataModel extends \InvalidArgumentException { } PK �X!]�ːЃ � Exception/TaskNotFound.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2020 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 2, or later */ namespace FOF30\Controller\Exception; defined('_JEXEC') || die; /** * Exception thrown when we can't find a suitable method to handle the requested task */ class TaskNotFound extends \InvalidArgumentException { } PK �]!]@��$� � OutputController.phpnu &1i� <?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Cache\CacheController; use Joomla\CMS\Log\Log; /** * Joomla Cache output type object * * @since 1.7.0 */ class OutputController extends CacheController { /** * Cache data ID * * @var string * @since 1.7.0 */ protected $_id; /** * Cache data group * * @var string * @since 1.7.0 */ protected $_group; /** * Object to test locked state * * @var \stdClass * @since 1.7.0 * @deprecated 4.0 */ protected $_locktest = null; /** * Start the cache * * @param string $id The cache data ID * @param string $group The cache data group * * @return boolean * * @since 1.7.0 * @deprecated 4.0 */ public function start($id, $group = null) { Log::add( __METHOD__ . '() is deprecated.', Log::WARNING, 'deprecated' ); // If we have data in cache use that. $data = $this->cache->get($id, $group); $this->_locktest = new \stdClass; $this->_locktest->locked = null; $this->_locktest->locklooped = null; if ($data === false) { $this->_locktest = $this->cache->lock($id, $group); if ($this->_locktest->locked == true && $this->_locktest->locklooped == true) { $data = $this->cache->get($id, $group); } } if ($data !== false) { $data = unserialize(trim($data)); echo $data; if ($this->_locktest->locked == true) { $this->cache->unlock($id, $group); } return true; } // Nothing in cache... let's start the output buffer and start collecting data for next time. if ($this->_locktest->locked == false) { $this->_locktest = $this->cache->lock($id, $group); } ob_start(); ob_implicit_flush(false); // Set id and group placeholders $this->_id = $id; $this->_group = $group; return false; } /** * Stop the cache buffer and store the cached data * * @return boolean True if the cache data was stored * * @since 1.7.0 * @deprecated 4.0 */ public function end() { Log::add( __METHOD__ . '() is deprecated.', Log::WARNING, 'deprecated' ); // Get data from output buffer and echo it $data = ob_get_clean(); echo $data; // Get the ID and group and reset the placeholders $id = $this->_id; $group = $this->_group; $this->_id = null; $this->_group = null; // Get the storage handler and store the cached data $ret = $this->cache->store(serialize($data), $id, $group); if ($this->_locktest->locked == true) { $this->cache->unlock($id, $group); } return $ret; } /** * Get stored cached data by ID and group * * @param string $id The cache data ID * @param string $group The cache data group * * @return mixed Boolean false on no result, cached object otherwise * * @since 1.7.0 */ public function get($id, $group = null) { $data = $this->cache->get($id, $group); if ($data === false) { $locktest = $this->cache->lock($id, $group); // If locklooped is true try to get the cached data again; it could exist now. if ($locktest->locked === true && $locktest->locklooped === true) { $data = $this->cache->get($id, $group); } if ($locktest->locked === true) { $this->cache->unlock($id, $group); } } // Check again because we might get it from second attempt if ($data !== false) { // Trim to fix unserialize errors $data = unserialize(trim($data)); } return $data; } /** * Store data to cache by ID and group * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $wrkarounds True to use wrkarounds * * @return boolean True if cache stored * * @since 1.7.0 */ public function store($data, $id, $group = null, $wrkarounds = true) { $locktest = $this->cache->lock($id, $group); if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return false; } $result = $this->cache->store(serialize($data), $id, $group); if ($locktest->locked === true) { $this->cache->unlock($id, $group); } return $result; } } PK �]!]�T\� CallbackController.phpnu &1i� <?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheController; /** * Joomla! Cache callback type object * * @since 1.7.0 */ class CallbackController extends CacheController { /** * Executes a cacheable callback if not found in cache else returns cached output and result * * Since arguments to this function are read with func_get_args you can pass any number of arguments to this method * as long as the first argument passed is the callback definition. * * The callback definition can be in several forms: * - Standard PHP Callback array see <https://www.php.net/callback> [recommended] * - Function name as a string eg. 'foo' for function foo() * - Static method name as a string eg. 'MyClass::myMethod' for method myMethod() of class MyClass * * @return mixed Result of the callback * * @since 1.7.0 * @deprecated 4.0 */ public function call() { // Get callback and arguments $args = func_get_args(); $callback = array_shift($args); return $this->get($callback, $args); } /** * Executes a cacheable callback if not found in cache else returns cached output and result * * @param mixed $callback Callback or string shorthand for a callback * @param array $args Callback arguments * @param mixed $id Cache ID * @param boolean $wrkarounds True to use workarounds * @param array $woptions Workaround options * * @return mixed Result of the callback * * @since 1.7.0 */ public function get($callback, $args = array(), $id = false, $wrkarounds = false, $woptions = array()) { // Normalize callback if (is_array($callback) || is_callable($callback)) { // We have a standard php callback array -- do nothing } elseif (strstr($callback, '::')) { // This is shorthand for a static method callback classname::methodname list ($class, $method) = explode('::', $callback); $callback = array(trim($class), trim($method)); } elseif (strstr($callback, '->')) { /* * This is a really not so smart way of doing this... we provide this for backward compatibility but this * WILL! disappear in a future version. If you are using this syntax change your code to use the standard * PHP callback array syntax: <https://www.php.net/callback> * * We have to use some silly global notation to pull it off and this is very unreliable */ list ($object_123456789, $method) = explode('->', $callback); global $$object_123456789; $callback = array($$object_123456789, $method); } if (!$id) { // Generate an ID $id = $this->_makeId($callback, $args); } $data = $this->cache->get($id); $locktest = (object) array('locked' => null, 'locklooped' => null); if ($data === false) { $locktest = $this->cache->lock($id); // If locklooped is true try to get the cached data again; it could exist now. if ($locktest->locked === true && $locktest->locklooped === true) { $data = $this->cache->get($id); } } if ($data !== false) { if ($locktest->locked === true) { $this->cache->unlock($id); } $data = unserialize(trim($data)); if ($wrkarounds) { echo Cache::getWorkarounds( $data['output'], array('mergehead' => isset($woptions['mergehead']) ? $woptions['mergehead'] : 0) ); } else { echo $data['output']; } return $data['result']; } if (!is_array($args)) { $referenceArgs = !empty($args) ? array(&$args) : array(); } else { $referenceArgs = &$args; } if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return call_user_func_array($callback, $referenceArgs); } $coptions = array(); if (isset($woptions['modulemode']) && $woptions['modulemode'] == 1) { $document = \JFactory::getDocument(); if (method_exists($document, 'getHeadData')) { $coptions['headerbefore'] = $document->getHeadData(); } $coptions['modulemode'] = 1; } else { $coptions['modulemode'] = 0; } $coptions['nopathway'] = isset($woptions['nopathway']) ? $woptions['nopathway'] : 1; $coptions['nohead'] = isset($woptions['nohead']) ? $woptions['nohead'] : 1; $coptions['nomodules'] = isset($woptions['nomodules']) ? $woptions['nomodules'] : 1; ob_start(); ob_implicit_flush(false); $result = call_user_func_array($callback, $referenceArgs); $output = ob_get_clean(); $data = array('result' => $result); if ($wrkarounds) { $data['output'] = Cache::setWorkarounds($output, $coptions); } else { $data['output'] = $output; } // Store the cache data $this->cache->store(serialize($data), $id); if ($locktest->locked === true) { $this->cache->unlock($id); } echo $output; return $result; } /** * Generate a callback cache ID * * @param callback $callback Callback to cache * @param array $args Arguments to the callback method to cache * * @return string MD5 Hash * * @since 1.7.0 */ protected function _makeId($callback, $args) { if (is_array($callback) && is_object($callback[0])) { $vars = get_object_vars($callback[0]); $vars[] = strtolower(get_class($callback[0])); $callback[0] = $vars; } // A Closure can't be serialized, so to generate the ID we'll need to get its hash if (is_a($callback, 'closure')) { $hash = spl_object_hash($callback); return md5($hash . serialize(array($args))); } return md5(serialize(array($callback, $args))); } } PK �]!]��7 7 PageController.phpnu &1i� <?php /** * Joomla! Content Management System * * @copyright (C) 2007 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheController; /** * Joomla! Cache page type object * * @since 1.7.0 */ class PageController extends CacheController { /** * ID property for the cache page object. * * @var integer * @since 1.7.0 */ protected $_id; /** * Cache group * * @var string * @since 1.7.0 */ protected $_group; /** * Cache lock test * * @var \stdClass * @since 1.7.0 */ protected $_locktest = null; /** * Get the cached page data * * @param boolean $id The cache data ID * @param string $group The cache data group * * @return mixed Boolean false on no result, cached object otherwise * * @since 1.7.0 */ public function get($id = false, $group = 'page') { // If an id is not given, generate it from the request if (!$id) { $id = $this->_makeId(); } // If the etag matches the page id ... set a no change header and exit : utilize browser cache if (!headers_sent() && isset($_SERVER['HTTP_IF_NONE_MATCH'])) { $etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']); if ($etag == $id) { $browserCache = isset($this->options['browsercache']) ? $this->options['browsercache'] : false; if ($browserCache) { $this->_noChange(); } } } // We got a cache hit... set the etag header and echo the page data $data = $this->cache->get($id, $group); $this->_locktest = (object) array('locked' => null, 'locklooped' => null); if ($data === false) { $this->_locktest = $this->cache->lock($id, $group); // If locklooped is true try to get the cached data again; it could exist now. if ($this->_locktest->locked === true && $this->_locktest->locklooped === true) { $data = $this->cache->get($id, $group); } } if ($data !== false) { if ($this->_locktest->locked === true) { $this->cache->unlock($id, $group); } $data = unserialize(trim($data)); $data = Cache::getWorkarounds($data); $this->_setEtag($id); return $data; } // Set ID and group placeholders $this->_id = $id; $this->_group = $group; return false; } /** * Stop the cache buffer and store the cached data * * @param mixed $data The data to store * @param string $id The cache data ID * @param string $group The cache data group * @param boolean $wrkarounds True to use workarounds * * @return boolean * * @since 1.7.0 */ public function store($data, $id, $group = null, $wrkarounds = true) { if ($this->_locktest->locked === false && $this->_locktest->locklooped === true) { // We can not store data because another process is in the middle of saving return false; } // Get page data from the application object if (!$data) { $data = \JFactory::getApplication()->getBody(); // Only attempt to store if page data exists. if (!$data) { return false; } } // Get id and group and reset the placeholders if (!$id) { $id = $this->_id; } if (!$group) { $group = $this->_group; } if ($wrkarounds) { $data = Cache::setWorkarounds( $data, array( 'nopathway' => 1, 'nohead' => 1, 'nomodules' => 1, 'headers' => true, ) ); } $result = $this->cache->store(serialize($data), $id, $group); if ($this->_locktest->locked === true) { $this->cache->unlock($id, $group); } return $result; } /** * Generate a page cache id * * @return string MD5 Hash * * @since 1.7.0 * @todo Discuss whether this should be coupled to a data hash or a request hash ... perhaps hashed with a serialized request */ protected function _makeId() { return Cache::makeId(); } /** * There is no change in page data so send an unmodified header and die gracefully * * @return void * * @since 1.7.0 */ protected function _noChange() { $app = \JFactory::getApplication(); // Send not modified header and exit gracefully $app->setHeader('Status', 304, true); $app->sendHeaders(); $app->close(); } /** * Set the ETag header in the response * * @param string $etag The entity tag (etag) to set * * @return void * * @since 1.7.0 */ protected function _setEtag($etag) { \JFactory::getApplication()->setHeader('ETag', '"' . $etag . '"', true); } } PK �]!]v> ViewController.phpnu &1i� <?php /** * Joomla! Content Management System * * @copyright (C) 2010 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\Cache\Controller; defined('JPATH_PLATFORM') or die; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\CacheController; /** * Joomla! Cache view type object * * @since 1.7.0 */ class ViewController extends CacheController { /** * Get the cached view data * * @param object $view The view object to cache output for * @param string $method The method name of the view method to cache output for * @param mixed $id The cache data ID * @param boolean $wrkarounds True to enable workarounds. * * @return boolean True if the cache is hit (false else) * * @since 1.7.0 */ public function get($view, $method = 'display', $id = false, $wrkarounds = true) { // If an id is not given generate it from the request if (!$id) { $id = $this->_makeId($view, $method); } $data = $this->cache->get($id); $locktest = (object) array('locked' => null, 'locklooped' => null); if ($data === false) { $locktest = $this->cache->lock($id); /* * If the loop is completed and returned true it means the lock has been set. * If looped is true try to get the cached data again; it could exist now. */ if ($locktest->locked === true && $locktest->locklooped === true) { $data = $this->cache->get($id); } // False means that locking is either turned off or maxtime has been exceeded. Execute the view. } if ($data !== false) { if ($locktest->locked === true) { $this->cache->unlock($id); } $data = unserialize(trim($data)); if ($wrkarounds) { echo Cache::getWorkarounds($data); } else { // No workarounds, so all data is stored in one piece echo $data; } return true; } // No hit so we have to execute the view if (!method_exists($view, $method)) { return false; } if ($locktest->locked === false && $locktest->locklooped === true) { // We can not store data because another process is in the middle of saving $view->$method(); return false; } // Capture and echo output ob_start(); ob_implicit_flush(false); $view->$method(); $data = ob_get_clean(); echo $data; /* * For a view we have a special case. We need to cache not only the output from the view, but the state * of the document head after the view has been rendered. This will allow us to properly cache any attached * scripts or stylesheets or links or any other modifications that the view has made to the document object */ if ($wrkarounds) { $data = Cache::setWorkarounds($data); } // Store the cache data $this->cache->store(serialize($data), $id); if ($locktest->locked === true) { $this->cache->unlock($id); } return false; } /** * Generate a view cache ID. * * @param object $view The view object to cache output for * @param string $method The method name to cache for the view object * * @return string MD5 Hash * * @since 1.7.0 */ protected function _makeId($view, $method) { return md5(serialize(array(Cache::makeId(), get_class($view), $method))); } } PK �X!]�e�/6{ 6{ Controller.phpnu &1i� PK �X!]9k_9� � t{ Mixin/PredefinedTaskList.phpnu &1i� PK �X!]�^�� � Y� DataController.phpnu &1i� PK �X!]�]:�n n � Exception/ItemNotFound.phpnu &1i� PK �X!]�W` ` Z Exception/CannotGetName.phpnu &1i� PK �X!]��_� � Exception/LockedRecord.phpnu &1i� PK �X!] �#m m �! Exception/NotADataModel.phpnu &1i� PK �X!]�ːЃ � �# Exception/TaskNotFound.phpnu &1i� PK �]!]@��$� � _% OutputController.phpnu &1i� PK �]!]�T\� `7 CallbackController.phpnu &1i� PK �]!]��7 7 �N PageController.phpnu &1i� PK �]!]v> $a ViewController.phpnu &1i� PK rn
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка