Файловый менеджер - Редактировать - /home/wuectly/www/03cbe/Controller.zip
Назад
PK At!]�s�+e +e FormController.phpnu &1i� <?php /** * Joomla! Content Management System * * @copyright (C) 2009 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; defined('JPATH_PLATFORM') or die; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; /** * Controller tailored to suit most form-based admin operations. * * @since 1.6 * @todo Add ability to set redirect manually to better cope with frontend usage. */ class FormController extends BaseController { /** * The context for storing internal data, e.g. record. * * @var string * @since 1.6 */ protected $context; /** * The URL option for the component. * * @var string * @since 1.6 */ protected $option; /** * The URL view item variable. * * @var string * @since 1.6 */ protected $view_item; /** * The URL view list variable. * * @var string * @since 1.6 */ protected $view_list; /** * The prefix to use with controller messages. * * @var string * @since 1.6 */ protected $text_prefix; /** * Constructor. * * @param array $config An optional associative array of configuration settings. * @param MVCFactoryInterface $factory The factory. * * @see \JControllerLegacy * @since 1.6 * @throws \Exception */ public function __construct($config = array(), MVCFactoryInterface $factory = null) { parent::__construct($config, $factory); // Guess the option as com_NameOfController if (empty($this->option)) { $this->option = 'com_' . strtolower($this->getName()); } // Guess the \JText message prefix. Defaults to the option. if (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } // Guess the context as the suffix, eg: OptionControllerContent. if (empty($this->context)) { $r = null; if (!preg_match('/(.*)Controller(.*)/i', get_class($this), $r)) { throw new \Exception(\JText::_('JLIB_APPLICATION_ERROR_CONTROLLER_GET_NAME'), 500); } $this->context = strtolower($r[2]); } // Guess the item view as the context. if (empty($this->view_item)) { $this->view_item = $this->context; } // Guess the list view as the plural of the item view. if (empty($this->view_list)) { // @TODO Probably worth moving to an inflector class based on // http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/ // Simple pluralisation based on public domain snippet by Paul Osman // For more complex types, just manually set the variable in your class. $plural = array( array('/(x|ch|ss|sh)$/i', "$1es"), array('/([^aeiouy]|qu)y$/i', "$1ies"), array('/([^aeiouy]|qu)ies$/i', "$1y"), array('/(bu)s$/i', "$1ses"), array('/s$/i', 's'), array('/$/', 's'), ); // Check for matches using regular expressions foreach ($plural as $pattern) { if (preg_match($pattern[0], $this->view_item)) { $this->view_list = preg_replace($pattern[0], $pattern[1], $this->view_item); break; } } } // Apply, Save & New, and Save As copy should be standard on forms. $this->registerTask('apply', 'save'); $this->registerTask('save2new', 'save'); $this->registerTask('save2copy', 'save'); $this->registerTask('editAssociations', 'save'); } /** * Method to add a new record. * * @return boolean True if the record can be added, false if not. * * @since 1.6 */ public function add() { $context = "$this->option.edit.$this->context"; // Access check. if (!$this->allowAdd()) { // Set the internal error and also the redirect error. $this->setError(\JText::_('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED')); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Clear the record edit information from the session. \JFactory::getApplication()->setUserState($context . '.data', null); // Redirect to the edit screen. $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend(), false ) ); return true; } /** * Method to check if you can add a new record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * * @return boolean * * @since 1.6 */ protected function allowAdd($data = array()) { $user = \JFactory::getUser(); return $user->authorise('core.create', $this->option) || count($user->getAuthorisedCategories($this->option, 'core.create')); } /** * Method to check if you can edit an existing record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * @param string $key The name of the key for the primary key; default is id. * * @return boolean * * @since 1.6 */ protected function allowEdit($data = array(), $key = 'id') { return \JFactory::getUser()->authorise('core.edit', $this->option); } /** * Method to check if you can save a new or existing record. * * Extended classes can override this if necessary. * * @param array $data An array of input data. * @param string $key The name of the key for the primary key. * * @return boolean * * @since 1.6 */ protected function allowSave($data, $key = 'id') { $recordId = isset($data[$key]) ? $data[$key] : '0'; if ($recordId) { return $this->allowEdit($data, $key); } else { return $this->allowAdd($data); } } /** * Method to run batch operations. * * @param \JModelLegacy $model The model of the component being processed. * * @return boolean True if successful, false otherwise and internal error is set. * * @since 1.7 */ public function batch($model) { $vars = $this->input->post->get('batch', array(), 'array'); $cid = (array) $this->input->post->get('cid', array(), 'int'); // Remove zero values resulting from input filter $cid = array_filter($cid); // Build an array of item contexts to check $contexts = array(); $option = isset($this->extension) ? $this->extension : $this->option; foreach ($cid as $id) { // If we're coming from com_categories, we need to use extension vs. option $contexts[$id] = $option . '.' . $this->context . '.' . $id; } // Attempt to run the batch operation. if ($model->batch($vars, $cid, $contexts)) { $this->setMessage(\JText::_('JLIB_APPLICATION_SUCCESS_BATCH')); return true; } else { $this->setMessage(\JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_FAILED', $model->getError()), 'warning'); return false; } } /** * Method to cancel an edit. * * @param string $key The name of the primary key of the URL variable. * * @return boolean True if access level checks pass, false otherwise. * * @since 1.6 */ public function cancel($key = null) { $this->checkToken(); $model = $this->getModel(); $table = $model->getTable(); $context = "$this->option.edit.$this->context"; if (empty($key)) { $key = $table->getKeyName(); } $recordId = $this->input->getInt($key); // Attempt to check-in the current record. if ($recordId && property_exists($table, 'checked_out') && $model->checkin($recordId) === false) { // Check-in failed, go back to the record and display a notice. $this->setError(\JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError())); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $key), false ) ); return false; } // Clean the session data and redirect. $this->releaseEditId($context, $recordId); \JFactory::getApplication()->setUserState($context . '.data', null); $url = 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(); // Check if there is a return value $return = $this->input->get('return', null, 'base64'); if (!is_null($return) && \JUri::isInternal(base64_decode($return))) { $url = base64_decode($return); } // Redirect to the list screen. $this->setRedirect(\JRoute::_($url, false)); return true; } /** * Method to edit an existing record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key * (sometimes required to avoid router collisions). * * @return boolean True if access level check and checkout passes, false otherwise. * * @since 1.6 */ public function edit($key = null, $urlVar = null) { // Do not cache the response to this, its a redirect, and mod_expires and google chrome browser bugs cache it forever! \JFactory::getApplication()->allowCache(false); $model = $this->getModel(); $table = $model->getTable(); $cid = (array) $this->input->post->get('cid', array(), 'int'); $context = "$this->option.edit.$this->context"; // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } // To avoid data collisions the urlVar may be different from the primary key. if (empty($urlVar)) { $urlVar = $key; } // Get the previous record id (if any) and the current record id. $recordId = (int) (count($cid) ? $cid[0] : $this->input->getInt($urlVar)); $checkin = property_exists($table, $table->getColumnAlias('checked_out')); // Access check. if (!$this->allowEdit(array($key => $recordId), $key)) { $this->setError(\JText::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED')); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Attempt to check-out the new record for editing and redirect. if ($checkin && !$model->checkout($recordId)) { // Check-out failed, display a notice but allow the user to see the record. $this->setError(\JText::sprintf('JLIB_APPLICATION_ERROR_CHECKOUT_FAILED', $model->getError())); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } else { // Check-out succeeded, push the new record id into the session. $this->holdEditId($context, $recordId); \JFactory::getApplication()->setUserState($context . '.data', null); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return true; } } /** * Method to get a model object, loading it if required. * * @param string $name The model name. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for model. Optional. * * @return \JModelLegacy The model. * * @since 1.6 */ public function getModel($name = '', $prefix = '', $config = array('ignore_request' => true)) { if (empty($name)) { $name = $this->context; } return parent::getModel($name, $prefix, $config); } /** * Gets the URL arguments to append to an item redirect. * * @param integer $recordId The primary key id for the item. * @param string $urlVar The name of the URL variable for the id. * * @return string The arguments to append to the redirect URL. * * @since 1.6 */ protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id') { $append = ''; // Setup redirect info. if ($tmpl = $this->input->get('tmpl', '', 'string')) { $append .= '&tmpl=' . $tmpl; } if ($layout = $this->input->get('layout', 'edit', 'string')) { $append .= '&layout=' . $layout; } if ($forcedLanguage = $this->input->get('forcedLanguage', '', 'cmd')) { $append .= '&forcedLanguage=' . $forcedLanguage; } if ($recordId) { $append .= '&' . $urlVar . '=' . $recordId; } $return = $this->input->get('return', null, 'base64'); if ($return) { $append .= '&return=' . $return; } return $append; } /** * Gets the URL arguments to append to a list redirect. * * @return string The arguments to append to the redirect URL. * * @since 1.6 */ protected function getRedirectToListAppend() { $append = ''; // Setup redirect info. if ($tmpl = $this->input->get('tmpl', '', 'string')) { $append .= '&tmpl=' . $tmpl; } if ($forcedLanguage = $this->input->get('forcedLanguage', '', 'cmd')) { $append .= '&forcedLanguage=' . $forcedLanguage; } return $append; } /** * Function that allows child controller access to model data * after the data has been saved. * * @param \JModelLegacy $model The data model object. * @param array $validData The validated data. * * @return void * * @since 1.6 */ protected function postSaveHook(\JModelLegacy $model, $validData = array()) { } /** * Method to load a row from version history * * @return mixed True if the record can be added, an error object if not. * * @since 3.2 */ public function loadhistory() { $model = $this->getModel(); $table = $model->getTable(); $historyId = $this->input->getInt('version_id', null); if (!$model->loadhistory($historyId, $table)) { $this->setMessage($model->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } $recordId = $table->$key; // To avoid data collisions the urlVar may be different from the primary key. $urlVar = empty($this->urlVar) ? $key : $this->urlVar; // Access check. if (!$this->allowEdit(array($key => $recordId), $key)) { $this->setError(\JText::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED')); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); $table->checkin(); return false; } $table->store(); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); $this->setMessage( \JText::sprintf( 'JLIB_APPLICATION_SUCCESS_LOAD_HISTORY', $model->getState('save_date'), $model->getState('version_note') ) ); // Invoke the postSave method to allow for the child class to access the model. $this->postSaveHook($model); return true; } /** * Method to save a record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). * * @return boolean True if successful, false otherwise. * * @since 1.6 */ public function save($key = null, $urlVar = null) { // Check for request forgeries. $this->checkToken(); $app = \JFactory::getApplication(); $model = $this->getModel(); $table = $model->getTable(); $data = $this->input->post->get('jform', array(), 'array'); $checkin = property_exists($table, $table->getColumnAlias('checked_out')); $context = "$this->option.edit.$this->context"; $task = $this->getTask(); // Determine the name of the primary key for the data. if (empty($key)) { $key = $table->getKeyName(); } // To avoid data collisions the urlVar may be different from the primary key. if (empty($urlVar)) { $urlVar = $key; } $recordId = $this->input->getInt($urlVar); // Populate the row id from the session. $data[$key] = $recordId; // The save2copy task needs to be handled slightly differently. if ($task === 'save2copy') { // Check-in the original row. if ($checkin && $model->checkin($data[$key]) === false) { // Check-in failed. Go back to the item and display a notice. $this->setError(\JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError())); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } // Reset the ID, the multilingual associations and then treat the request as for Apply. $data[$key] = 0; $data['associations'] = array(); $task = 'apply'; } // Access check. if (!$this->allowSave($data, $key)) { $this->setError(\JText::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED')); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); return false; } // Validate the posted data. // Sometimes the form needs some posted data, such as for plugins and modules. $form = $model->getForm($data, false); if (!$form) { $app->enqueueMessage($model->getError(), 'error'); return false; } // Send an object which can be modified through the plugin event $objData = (object) $data; $app->triggerEvent( 'onContentNormaliseRequestData', array($this->option . '.' . $this->context, $objData, $form) ); $data = (array) $objData; // Test whether the data is valid. $validData = $model->validate($form, $data); // Check for validation errors. if ($validData === false) { // Get the validation messages. $errors = $model->getErrors(); // Push up to three validation messages out to the user. for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++) { if ($errors[$i] instanceof \Exception) { $app->enqueueMessage($errors[$i]->getMessage(), 'warning'); } else { $app->enqueueMessage($errors[$i], 'warning'); } } /** * We need the filtered value of calendar fields because the UTC normalision is * done in the filter and on output. This would apply the Timezone offset on * reload. We set the calendar values we save to the processed date. */ $filteredData = $form->filter($data); foreach ($form->getFieldset() as $field) { if ($field->type === 'Calendar') { $fieldName = $field->fieldname; if (isset($filteredData[$fieldName])) { $data[$fieldName] = $filteredData[$fieldName]; } } } // Save the data in the session. $app->setUserState($context . '.data', $data); // Redirect back to the edit screen. $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } if (!isset($validData['tags'])) { $validData['tags'] = null; } // Attempt to save the data. if (!$model->save($validData)) { // Save the data in the session. $app->setUserState($context . '.data', $validData); // Redirect back to the edit screen. $this->setError(\JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError())); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } // Save succeeded, so check-in the record. if ($checkin && $model->checkin($validData[$key]) === false) { // Save the data in the session. $app->setUserState($context . '.data', $validData); // Check-in failed, so go back to the record and display a notice. $this->setError(\JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError())); $this->setMessage($this->getError(), 'error'); $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); return false; } $langKey = $this->text_prefix . ($recordId === 0 && $app->isClient('site') ? '_SUBMIT' : '') . '_SAVE_SUCCESS'; $prefix = \JFactory::getLanguage()->hasKey($langKey) ? $this->text_prefix : 'JLIB_APPLICATION'; $this->setMessage(\JText::_($prefix . ($recordId === 0 && $app->isClient('site') ? '_SUBMIT' : '') . '_SAVE_SUCCESS')); // Redirect the user and adjust session state based on the chosen task. switch ($task) { case 'apply': // Set the record data in the session. $recordId = $model->getState($this->context . '.id'); $this->holdEditId($context, $recordId); $app->setUserState($context . '.data', null); $model->checkout($recordId); // Redirect back to the edit screen. $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ) ); break; case 'save2new': // Clear the record id and data from the session. $this->releaseEditId($context, $recordId); $app->setUserState($context . '.data', null); // Redirect back to the edit screen. $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend(null, $urlVar), false ) ); break; default: // Clear the record id and data from the session. $this->releaseEditId($context, $recordId); $app->setUserState($context . '.data', null); $url = 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(); // Check if there is a return value $return = $this->input->get('return', null, 'base64'); if (!is_null($return) && \JUri::isInternal(base64_decode($return))) { $url = base64_decode($return); } // Redirect to the list screen. $this->setRedirect(\JRoute::_($url, false)); break; } // Invoke the postSave method to allow for the child class to access the model. $this->postSaveHook($model, $validData); return true; } /** * Method to reload a record. * * @param string $key The name of the primary key of the URL variable. * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions). * * @return void * * @since 3.7.4 */ public function reload($key = null, $urlVar = null) { // Check for request forgeries. $this->checkToken(); $app = \JFactory::getApplication(); $model = $this->getModel(); $data = $this->input->post->get('jform', array(), 'array'); // Determine the name of the primary key for the data. if (empty($key)) { $key = $model->getTable()->getKeyName(); } // To avoid data collisions the urlVar may be different from the primary key. if (empty($urlVar)) { $urlVar = $key; } $recordId = $this->input->getInt($urlVar); // Populate the row id from the session. $data[$key] = $recordId; // Check if it is allowed to edit or create the data if (($recordId && !$this->allowEdit($data, $key)) || (!$recordId && !$this->allowAdd($data))) { $this->setRedirect( \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_list . $this->getRedirectToListAppend(), false ) ); $this->redirect(); } // The redirect url $redirectUrl = \JRoute::_( 'index.php?option=' . $this->option . '&view=' . $this->view_item . $this->getRedirectToItemAppend($recordId, $urlVar), false ); /* @var \JForm $form */ $form = $model->getForm($data, false); /** * We need the filtered value of calendar fields because the UTC normalision is * done in the filter and on output. This would apply the Timezone offset on * reload. We set the calendar values we save to the processed date. */ $filteredData = $form->filter($data); foreach ($form->getFieldset() as $field) { if ($field->type === 'Calendar') { $fieldName = $field->fieldname; if ($field->group) { if (isset($filteredData[$field->group][$fieldName])) { $data[$field->group][$fieldName] = $filteredData[$field->group][$fieldName]; } } else { if (isset($filteredData[$fieldName])) { $data[$fieldName] = $filteredData[$fieldName]; } } } } // Save the data in the session. $app->setUserState($this->option . '.edit.' . $this->context . '.data', $data); $this->setRedirect($redirectUrl); $this->redirect(); } /** * Load item to edit associations in com_associations * * @return void * * @since 3.9.0 * * @deprecated 5.0 It is handled by regular save method now. */ public function editAssociations() { // Initialise variables. $app = \JFactory::getApplication(); $input = $app->input; $model = $this->getModel(); $data = $input->get('jform', array(), 'array'); $model->editAssociations($data); } } PK At!]��R�Od Od BaseController.phpnu &1i� <?php /** * Joomla! Content Management System * * @copyright (C) 2006 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ namespace Joomla\CMS\MVC\Controller; use Joomla\CMS\MVC\Factory\LegacyFactory; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; defined('JPATH_PLATFORM') or die; /** * Base class for a Joomla Controller * * Controller (Controllers are where you put all the actual code.) Provides basic * functionality, such as rendering views (aka displaying templates). * * @since 2.5.5 */ class BaseController extends \JObject { /** * The base path of the controller * * @var string * @since 3.0 */ protected $basePath; /** * The default view for the display method. * * @var string * @since 3.0 */ protected $default_view; /** * The mapped task that was performed. * * @var string * @since 3.0 */ protected $doTask; /** * Redirect message. * * @var string * @since 3.0 */ protected $message; /** * Redirect message type. * * @var string * @since 3.0 */ protected $messageType; /** * Array of class methods * * @var array * @since 3.0 */ protected $methods; /** * The name of the controller * * @var array * @since 3.0 */ protected $name; /** * The prefix of the models * * @var string * @since 3.0 */ protected $model_prefix; /** * The set of search directories for resources (views). * * @var array * @since 3.0 */ protected $paths; /** * URL for redirection. * * @var string * @since 3.0 */ protected $redirect; /** * Current or most recently performed task. * * @var string * @since 3.0 */ protected $task; /** * Array of class methods to call for a given task. * * @var array * @since 3.0 */ protected $taskMap; /** * Hold a JInput object for easier access to the input variables. * * @var \JInput * @since 3.0 */ protected $input; /** * The factory. * * @var MVCFactoryInterface * @since 3.10.0 */ protected $factory; /** * Instance container. * * @var \JControllerLegacy * @since 3.0 */ protected static $instance; /** * Instance container containing the views. * * @var \JViewLegacy[] * @since 3.4 */ protected static $views; /** * Adds to the stack of model paths in LIFO order. * * @param mixed $path The directory (string), or list of directories (array) to add. * @param string $prefix A prefix for models * * @return void * * @since 3.0 */ public static function addModelPath($path, $prefix = '') { \JModelLegacy::addIncludePath($path, $prefix); } /** * Create the filename for a resource. * * @param string $type The resource type to create the filename for. * @param array $parts An associative array of filename information. Optional. * * @return string The filename. * * @since 3.0 */ public static function createFileName($type, $parts = array()) { $filename = ''; switch ($type) { case 'controller': if (!empty($parts['format'])) { if ($parts['format'] === 'html') { $parts['format'] = ''; } else { $parts['format'] = '.' . $parts['format']; } } else { $parts['format'] = ''; } $filename = strtolower($parts['name'] . $parts['format'] . '.php'); break; case 'view': if (!empty($parts['type'])) { $parts['type'] = '.' . $parts['type']; } else { $parts['type'] = ''; } $filename = strtolower($parts['name'] . '/view' . $parts['type'] . '.php'); break; } return $filename; } /** * Method to get a singleton controller instance. * * @param string $prefix The prefix for the controller. * @param array $config An array of optional constructor options. * * @return \JControllerLegacy * * @since 3.0 * @throws \Exception if the controller cannot be loaded. */ public static function getInstance($prefix, $config = array()) { if (is_object(self::$instance)) { return self::$instance; } $input = \JFactory::getApplication()->input; // Get the environment configuration. $basePath = array_key_exists('base_path', $config) ? $config['base_path'] : JPATH_COMPONENT; $format = $input->getWord('format'); $command = $input->get('task', 'display'); // Check for array format. $filter = \JFilterInput::getInstance(); if (is_array($command)) { $command = $filter->clean(array_pop(array_keys($command)), 'cmd'); } else { $command = $filter->clean($command, 'cmd'); } // Check for a controller.task command. if (strpos($command, '.') !== false) { // Explode the controller.task command. list ($type, $task) = explode('.', $command); // Define the controller filename and path. $file = self::createFileName('controller', array('name' => $type, 'format' => $format)); $path = $basePath . '/controllers/' . $file; $backuppath = $basePath . '/controller/' . $file; // Reset the task without the controller context. $input->set('task', $task); } else { // Base controller. $type = ''; // Define the controller filename and path. $file = self::createFileName('controller', array('name' => 'controller', 'format' => $format)); $path = $basePath . '/' . $file; $backupfile = self::createFileName('controller', array('name' => 'controller')); $backuppath = $basePath . '/' . $backupfile; } // Get the controller class name. $class = ucfirst($prefix) . 'Controller' . ucfirst($type); // Include the class if not present. if (!class_exists($class)) { // If the controller file path exists, include it. if (file_exists($path)) { require_once $path; } elseif (isset($backuppath) && file_exists($backuppath)) { require_once $backuppath; } else { throw new \InvalidArgumentException(\JText::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER', $type, $format)); } } // Instantiate the class. if (!class_exists($class)) { throw new \InvalidArgumentException(\JText::sprintf('JLIB_APPLICATION_ERROR_INVALID_CONTROLLER_CLASS', $class)); } // Instantiate the class, store it to the static container, and return it return self::$instance = new $class($config); } /** * Constructor. * * @param array $config An optional associative array of configuration settings. * Recognized key values include 'name', 'default_task', 'model_path', and * 'view_path' (this list is not meant to be comprehensive). * @param MVCFactoryInterface $factory The factory. * * @since 3.0 */ public function __construct($config = array(), MVCFactoryInterface $factory = null) { $this->methods = array(); $this->message = null; $this->messageType = 'message'; $this->paths = array(); $this->redirect = null; $this->taskMap = array(); if (defined('JDEBUG') && JDEBUG) { \JLog::addLogger(array('text_file' => 'jcontroller.log.php'), \JLog::ALL, array('controller')); } $this->input = \JFactory::getApplication()->input; // Determine the methods to exclude from the base class. $xMethods = get_class_methods('\JControllerLegacy'); // 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(); // Add default display method if not explicitly declared. if ($mName === 'display' || !in_array($mName, $xMethods)) { $this->methods[] = strtolower($mName); // Auto register the methods as tasks. $this->taskMap[strtolower($mName)] = $mName; } } // Set the view name if (empty($this->name)) { if (array_key_exists('name', $config)) { $this->name = $config['name']; } else { $this->name = $this->getName(); } } // Set a base path for use by the controller if (array_key_exists('base_path', $config)) { $this->basePath = $config['base_path']; } else { $this->basePath = JPATH_COMPONENT; } // If the default task is set, register it as such if (array_key_exists('default_task', $config)) { $this->registerDefaultTask($config['default_task']); } else { $this->registerDefaultTask('display'); } // Set the models prefix if (empty($this->model_prefix)) { if (array_key_exists('model_prefix', $config)) { // User-defined prefix $this->model_prefix = $config['model_prefix']; } else { $this->model_prefix = ucfirst($this->name) . 'Model'; } } // Set the default model search path if (array_key_exists('model_path', $config)) { // User-defined dirs $this->addModelPath($config['model_path'], $this->model_prefix); } else { $this->addModelPath($this->basePath . '/models', $this->model_prefix); } // Set the default view search path if (array_key_exists('view_path', $config)) { // User-defined dirs $this->setPath('view', $config['view_path']); } else { $this->setPath('view', $this->basePath . '/views'); } // Set the default view. if (array_key_exists('default_view', $config)) { $this->default_view = $config['default_view']; } elseif (empty($this->default_view)) { $this->default_view = $this->getName(); } $this->factory = $factory ? : new LegacyFactory; } /** * Adds to the search path for templates and resources. * * @param string $type The path type (e.g. 'model', 'view'). * @param mixed $path The directory string or stream array to search. * * @return \JControllerLegacy A \JControllerLegacy object to support chaining. * * @since 3.0 */ protected function addPath($type, $path) { if (!isset($this->paths[$type])) { $this->paths[$type] = array(); } // Loop through the path directories foreach ((array) $path as $dir) { // No surrounding spaces allowed! $dir = rtrim(\JPath::check($dir), '/') . '/'; // Add to the top of the search dirs array_unshift($this->paths[$type], $dir); } return $this; } /** * Add one or more view paths to the controller's stack, in LIFO order. * * @param mixed $path The directory (string) or list of directories (array) to add. * * @return \JControllerLegacy This object to support chaining. * * @since 3.0 */ public function addViewPath($path) { return $this->addPath('view', $path); } /** * Authorisation check * * @param string $task The ACO Section Value to check access on. * * @return boolean True if authorised * * @since 3.0 * @deprecated 3.0 Use \JAccess instead. */ public function authorise($task) { \JLog::add(__METHOD__ . ' is deprecated. Use \JAccess instead.', \JLog::WARNING, 'deprecated'); return true; } /** * Method to check whether an ID is in the edit list. * * @param string $context The context for the session storage. * @param integer $id The ID of the record to add to the edit list. * * @return boolean True if the ID is in the edit list. * * @since 3.0 */ protected function checkEditId($context, $id) { if ($id) { $values = (array) \JFactory::getApplication()->getUserState($context . '.id'); $result = in_array((int) $id, $values); if (defined('JDEBUG') && JDEBUG) { \JLog::add( sprintf( 'Checking edit ID %s.%s: %d %s', $context, $id, (int) $result, str_replace("\n", ' ', print_r($values, 1)) ), \JLog::INFO, 'controller' ); } return $result; } // No id for a new item. return true; } /** * Method to load and return a model object. * * @param string $name The name of the model. * @param string $prefix Optional model prefix. * @param array $config Configuration array for the model. Optional. * * @return \JModelLegacy|boolean Model object on success; otherwise false on failure. * * @since 3.0 */ protected function createModel($name, $prefix = '', $config = array()) { $model = $this->factory->createModel($name, $prefix, $config); if ($model === null) { return false; } return $model; } /** * Method to load and return a view object. This method first looks in the * current template directory for a match and, failing that, uses a default * set path to load the view class file. * * Note the "name, prefix, type" order of parameters, which differs from the * "name, type, prefix" order used in related public methods. * * @param string $name The name of the view. * @param string $prefix Optional prefix for the view class name. * @param string $type The type of view. * @param array $config Configuration array for the view. Optional. * * @return \JViewLegacy|null View object on success; null or error result on failure. * * @since 3.0 * @throws \Exception */ protected function createView($name, $prefix = '', $type = '', $config = array()) { $config['paths'] = $this->paths['view']; return $this->factory->createView($name, $prefix, $type, $config); } /** * Typical view method for MVC based architecture * * This function is provide as a default implementation, in most cases * you will need to override it in your own controllers. * * @param boolean $cachable If true, the view output will be cached * @param array $urlparams An array of safe URL parameters and their variable types, for valid values see {@link \JFilterInput::clean()}. * * @return \JControllerLegacy A \JControllerLegacy object to support chaining. * * @since 3.0 */ public function display($cachable = false, $urlparams = array()) { $document = \JFactory::getDocument(); $viewType = $document->getType(); $viewName = $this->input->get('view', $this->default_view); $viewLayout = $this->input->get('layout', 'default', 'string'); $view = $this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout)); // Get/Create the model if ($model = $this->getModel($viewName)) { // Push the model into the view (as default) $view->setModel($model, true); } $view->document = $document; // Display the view if ($cachable && $viewType !== 'feed' && \JFactory::getConfig()->get('caching') >= 1) { $option = $this->input->get('option'); if (is_array($urlparams)) { $app = \JFactory::getApplication(); 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; } $app->registeredurlparams = $registeredurlparams; } try { /** @var \JCacheControllerView $cache */ $cache = \JFactory::getCache($option, 'view'); $cache->get($view, 'display'); } catch (\JCacheException $exception) { $view->display(); } } else { $view->display(); } return $this; } /** * Execute a task by triggering a method in the derived class. * * @param string $task The task to perform. If no matching task is found, the '__default' task is executed, if defined. * * @return mixed The value returned by the called method. * * @since 3.0 * @throws \Exception */ public function execute($task) { $this->task = $task; $task = strtolower((string) $task); if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { throw new \Exception(\JText::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404); } // Record the actual task being fired $this->doTask = $doTask; return $this->$doTask(); } /** * Method to get a model object, loading it if required. * * @param string $name The model name. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for model. Optional. * * @return \JModelLegacy|boolean Model object on success; otherwise false on failure. * * @since 3.0 */ public function getModel($name = '', $prefix = '', $config = array()) { if (empty($name)) { $name = $this->getName(); } if (empty($prefix)) { $prefix = $this->model_prefix; } if ($model = $this->createModel($name, $prefix, $config)) { // Task is a reserved state $model->setState('task', $this->task); // Let's get the application object and set menu information if it's available $menu = \JFactory::getApplication()->getMenu(); if (is_object($menu) && $item = $menu->getActive()) { $params = $menu->getParams($item->id); // Set default state data $model->setState('parameters.menu', $params); } } return $model; } /** * Method to get the controller name * * The dispatcher 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 dispatcher * * @since 3.0 * @throws \Exception */ public function getName() { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)Controller/i', get_class($this), $r)) { throw new \Exception(\JText::_('JLIB_APPLICATION_ERROR_CONTROLLER_GET_NAME'), 500); } $this->name = strtolower($r[1]); } 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. * * @since 3.0 */ public function getTask() { return $this->task; } /** * Gets the available tasks in the controller. * * @return array Array[i] of task names. * * @since 3.0 */ public function getTasks() { return $this->methods; } /** * Method to get a reference to the current view and load it if necessary. * * @param string $name The view name. Optional, defaults to the controller name. * @param string $type The view type. Optional. * @param string $prefix The class prefix. Optional. * @param array $config Configuration array for view. Optional. * * @return \JViewLegacy Reference to the view or an error. * * @since 3.0 * @throws \Exception */ public function getView($name = '', $type = '', $prefix = '', $config = array()) { // @note We use self so we only access stuff in this class rather than in all classes. if (!isset(self::$views)) { self::$views = array(); } if (empty($name)) { $name = $this->getName(); } if (empty($prefix)) { $prefix = $this->getName() . 'View'; } if (empty(self::$views[$name][$type][$prefix])) { if ($view = $this->createView($name, $prefix, $type, $config)) { self::$views[$name][$type][$prefix] = & $view; } else { throw new \Exception(\JText::sprintf('JLIB_APPLICATION_ERROR_VIEW_NOT_FOUND', $name, $type, $prefix), 404); } } return self::$views[$name][$type][$prefix]; } /** * Method to add a record ID to the edit list. * * @param string $context The context for the session storage. * @param integer $id The ID of the record to add to the edit list. * * @return void * * @since 3.0 */ protected function holdEditId($context, $id) { $app = \JFactory::getApplication(); $values = (array) $app->getUserState($context . '.id'); // Add the id to the list if non-zero. if (!empty($id)) { $values[] = (int) $id; $values = array_unique($values); $app->setUserState($context . '.id', $values); if (defined('JDEBUG') && JDEBUG) { \JLog::add( sprintf( 'Holding edit ID %s.%s %s', $context, $id, str_replace("\n", ' ', print_r($values, 1)) ), \JLog::INFO, 'controller' ); } } } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. * * @since 3.0 */ public function redirect() { if ($this->redirect) { $app = \JFactory::getApplication(); // Enqueue the redirect message $app->enqueueMessage($this->message, $this->messageType); // Execute the redirect $app->redirect($this->redirect); } 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 \JControllerLegacy A \JControllerLegacy object to support chaining. * * @since 3.0 */ 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 \JControllerLegacy A \JControllerLegacy object to support chaining. * * @since 3.0 */ public function registerTask($task, $method) { if (in_array(strtolower($method), $this->methods)) { $this->taskMap[strtolower($task)] = $method; } return $this; } /** * Unregister (unmap) a task in the class. * * @param string $task The task. * * @return \JControllerLegacy This object to support chaining. * * @since 3.0 */ public function unregisterTask($task) { unset($this->taskMap[strtolower($task)]); return $this; } /** * Method to check whether an ID is in the edit list. * * @param string $context The context for the session storage. * @param integer $id The ID of the record to add to the edit list. * * @return void * * @since 3.0 */ protected function releaseEditId($context, $id) { $app = \JFactory::getApplication(); $values = (array) $app->getUserState($context . '.id'); // Do a strict search of the edit list values. $index = array_search((int) $id, $values, true); if (is_int($index)) { unset($values[$index]); $app->setUserState($context . '.id', $values); if (defined('JDEBUG') && JDEBUG) { \JLog::add( sprintf( 'Releasing edit ID %s.%s %s', $context, $id, str_replace("\n", ' ', print_r($values, 1)) ), \JLog::INFO, 'controller' ); } } } /** * 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 * * @since 3.0 */ public function setMessage($text, $type = 'message') { $previous = $this->message; $this->message = $text; $this->messageType = $type; return $previous; } /** * Sets an entire array of search paths for resources. * * @param string $type The type of path to set, typically 'view' or 'model'. * @param string $path The new set of search paths. If null or false, resets to the current directory only. * * @return void * * @since 3.0 */ protected function setPath($type, $path) { // Clear out the prior search dirs $this->paths[$type] = array(); // Actually add the user-specified directories $this->addPath($type, $path); } /** * Checks for a form token in the request. * * Use in conjunction with \JHtml::_('form.token') or \JSession::getFormToken. * * @param string $method The request method in which to look for the token key. * @param boolean $redirect Whether to implicitly redirect user to the referrer page on failure or simply return false. * * @return boolean True if found and valid, otherwise return false or redirect to referrer page. * * @since 3.7.0 * @see \JSession::checkToken() */ public function checkToken($method = 'post', $redirect = true) { $valid = \JSession::checkToken($method); if (!$valid && $redirect) { $referrer = $this->input->server->getString('HTTP_REFERER'); if (!\JUri::isInternal($referrer)) { $referrer = 'index.php'; } $app = \JFactory::getApplication(); $app->enqueueMessage(\JText::_('JINVALID_TOKEN_NOTICE'), 'warning'); $app->redirect($referrer); } return $valid; } /** * 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 \JControllerLegacy This object to support chaining. * * @since 3.0 */ public function setRedirect($url, $msg = null, $type = null) { $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($type)) { if (empty($this->messageType)) { $this->messageType = 'message'; } } // If the type is explicitly set, set it. else { $this->messageType = $type; } return $this; } } PK At!]���"�% �% AdminController.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\MVC\Controller; defined('JPATH_PLATFORM') or die; use Joomla\CMS\MVC\Factory\MVCFactoryInterface; use Joomla\Utilities\ArrayHelper; /** * Base class for a Joomla Administrator Controller * * Controller (controllers are where you put all the actual code) Provides basic * functionality, such as rendering views (aka displaying templates). * * @since 1.6 */ class AdminController extends BaseController { /** * The URL option for the component. * * @var string * @since 1.6 */ protected $option; /** * The prefix to use with controller messages. * * @var string * @since 1.6 */ protected $text_prefix; /** * The URL view list variable. * * @var string * @since 1.6 */ protected $view_list; /** * Constructor. * * @param array $config An optional associative array of configuration settings. * @param MVCFactoryInterface $factory The factory. * * @see \JControllerLegacy * @since 1.6 * @throws \Exception */ public function __construct($config = array(), MVCFactoryInterface $factory = null) { parent::__construct($config, $factory); // Define standard task mappings. // Value = 0 $this->registerTask('unpublish', 'publish'); // Value = 2 $this->registerTask('archive', 'publish'); // Value = -2 $this->registerTask('trash', 'publish'); // Value = -3 $this->registerTask('report', 'publish'); $this->registerTask('orderup', 'reorder'); $this->registerTask('orderdown', 'reorder'); // Guess the option as com_NameOfController. if (empty($this->option)) { $this->option = 'com_' . strtolower($this->getName()); } // Guess the \JText message prefix. Defaults to the option. if (empty($this->text_prefix)) { $this->text_prefix = strtoupper($this->option); } // Guess the list view as the suffix, eg: OptionControllerSuffix. if (empty($this->view_list)) { $r = null; if (!preg_match('/(.*)Controller(.*)/i', get_class($this), $r)) { throw new \Exception(\JText::_('JLIB_APPLICATION_ERROR_CONTROLLER_GET_NAME'), 500); } $this->view_list = strtolower($r[2]); } } /** * Removes an item. * * @return void * * @since 1.6 */ public function delete() { // Check for request forgeries $this->checkToken(); // Get items to remove from the request. $cid = (array) $this->input->get('cid', array(), 'int'); // Remove zero values resulting from input filter $cid = array_filter($cid); if (empty($cid)) { \JLog::add(\JText::_($this->text_prefix . '_NO_ITEM_SELECTED'), \JLog::WARNING, 'jerror'); } else { // Get the model. $model = $this->getModel(); // Remove the items. if ($model->delete($cid)) { $this->setMessage(\JText::plural($this->text_prefix . '_N_ITEMS_DELETED', count($cid))); } else { $this->setMessage($model->getError(), 'error'); } // Invoke the postDelete method to allow for the child class to access the model. $this->postDeleteHook($model, $cid); } $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false)); } /** * Function that allows child controller access to model data * after the item has been deleted. * * @param \JModelLegacy $model The data model object. * @param integer $id The validated data. * * @return void * * @since 3.1 */ protected function postDeleteHook(\JModelLegacy $model, $id = null) { } /** * Method to publish a list of items * * @return void * * @since 1.6 */ public function publish() { // Check for request forgeries $this->checkToken(); // Get items to publish from the request. $cid = (array) $this->input->get('cid', array(), 'int'); $data = array('publish' => 1, 'unpublish' => 0, 'archive' => 2, 'trash' => -2, 'report' => -3); $task = $this->getTask(); $value = ArrayHelper::getValue($data, $task, 0, 'int'); // Remove zero values resulting from input filter $cid = array_filter($cid); if (empty($cid)) { \JLog::add(\JText::_($this->text_prefix . '_NO_ITEM_SELECTED'), \JLog::WARNING, 'jerror'); } else { // Get the model. $model = $this->getModel(); // Publish the items. try { $model->publish($cid, $value); $errors = $model->getErrors(); $ntext = null; if ($value === 1) { if ($errors) { \JFactory::getApplication()->enqueueMessage(\JText::plural($this->text_prefix . '_N_ITEMS_FAILED_PUBLISHING', count($cid)), 'error'); } else { $ntext = $this->text_prefix . '_N_ITEMS_PUBLISHED'; } } elseif ($value === 0) { $ntext = $this->text_prefix . '_N_ITEMS_UNPUBLISHED'; } elseif ($value === 2) { $ntext = $this->text_prefix . '_N_ITEMS_ARCHIVED'; } else { $ntext = $this->text_prefix . '_N_ITEMS_TRASHED'; } if ($ntext !== null) { $this->setMessage(\JText::plural($ntext, count($cid))); } } catch (\Exception $e) { $this->setMessage($e->getMessage(), 'error'); } } $extension = $this->input->get('extension'); $extensionURL = $extension ? '&extension=' . $extension : ''; $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list . $extensionURL, false)); } /** * Changes the order of one or more records. * * @return boolean True on success * * @since 1.6 */ public function reorder() { // Check for request forgeries. $this->checkToken(); $ids = (array) $this->input->post->get('cid', array(), 'int'); $inc = $this->getTask() === 'orderup' ? -1 : 1; // Remove zero values resulting from input filter $ids = array_filter($ids); $model = $this->getModel(); $return = $model->reorder($ids, $inc); if ($return === false) { // Reorder failed. $message = \JText::sprintf('JLIB_APPLICATION_ERROR_REORDER_FAILED', $model->getError()); $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false), $message, 'error'); return false; } else { // Reorder succeeded. $message = \JText::_('JLIB_APPLICATION_SUCCESS_ITEM_REORDERED'); $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false), $message); return true; } } /** * Method to save the submitted ordering values for records. * * @return boolean True on success * * @since 1.6 */ public function saveorder() { // Check for request forgeries. $this->checkToken(); // Get the input $pks = (array) $this->input->post->get('cid', array(), 'int'); $order = (array) $this->input->post->get('order', array(), 'int'); // Remove zero PK's and corresponding order values resulting from input filter for PK foreach ($pks as $i => $pk) { if ($pk === 0) { unset($pks[$i]); unset($order[$i]); } } // Get the model $model = $this->getModel(); // Save the ordering $return = $model->saveorder($pks, $order); if ($return === false) { // Reorder failed $message = \JText::sprintf('JLIB_APPLICATION_ERROR_REORDER_FAILED', $model->getError()); $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false), $message, 'error'); return false; } else { // Reorder succeeded. $this->setMessage(\JText::_('JLIB_APPLICATION_SUCCESS_ORDERING_SAVED')); $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false)); return true; } } /** * Check in of one or more records. * * @return boolean True on success * * @since 1.6 */ public function checkin() { // Check for request forgeries. $this->checkToken(); $ids = (array) $this->input->post->get('cid', array(), 'int'); // Remove zero values resulting from input filter $ids = array_filter($ids); $model = $this->getModel(); $return = $model->checkin($ids); if ($return === false) { // Checkin failed. $message = \JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()); $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false), $message, 'error'); return false; } else { // Checkin succeeded. $message = \JText::plural($this->text_prefix . '_N_ITEMS_CHECKED_IN', count($ids)); $this->setRedirect(\JRoute::_('index.php?option=' . $this->option . '&view=' . $this->view_list, false), $message); return true; } } /** * Method to save the submitted ordering values for records via AJAX. * * @return void * * @since 3.0 */ public function saveOrderAjax() { // Check for request forgeries. $this->checkToken(); // Get the input $pks = (array) $this->input->post->get('cid', array(), 'int'); $order = (array) $this->input->post->get('order', array(), 'int'); // Remove zero PK's and corresponding order values resulting from input filter for PK foreach ($pks as $i => $pk) { if ($pk === 0) { unset($pks[$i]); unset($order[$i]); } } // Get the model $model = $this->getModel(); // Save the ordering $return = $model->saveorder($pks, $order); if ($return) { echo '1'; } // Close the application \JFactory::getApplication()->close(); } } PK f}!]�T;7� 7� DataController.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Controller\Exception\ItemNotFound; use FOF40\Controller\Exception\LockedRecord; use FOF40\Controller\Exception\NotADataModel; use FOF40\Controller\Exception\NotADataView; use FOF40\Controller\Exception\TaskNotFound; use FOF40\Model\DataModel; use FOF40\View\DataView\DataViewInterface; use FOF40\View\View; use Joomla\CMS\Language\Text; use Joomla\CMS\Response\JsonResponse; /** * Database-aware Controller * * @property-read \FOF40\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 null|array */ protected $cacheParams = null; /** * 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'])) { return; } if (!is_array($config['taskPrivileges'])) { return; } $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(string $task) { if ($task == 'default') { $task = $this->getCrudTask(); } return parent::execute($task); } /** * Returns a named View object * * @param string $name The View name. If null we'll use the viewName * variable or, if it's empty, the same name as * the Controller * @param array $config Configuration parameters to the View. If skipped * we will use $this->config * * @return View|DataViewInterface The instance of the View known to this Controller * * @throws NotADataView If the View found does not implement the DataViewInterface */ public function getView(?string $name = null, array $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); } if (($this->viewInstances[$viewName] instanceof View) && !($this->viewInstances[$viewName] instanceof DataViewInterface)) { throw new NotADataView(); } return $this->viewInstances[$viewName]; } /** * Implements a default browse task, i.e. read a bunch of records and send * them to the browser. * * @return void * @throws Exception */ public function browse(): void { // Initialise the savestate $saveState = $this->input->get('savestate', -999, 'int'); if ($saveState == -999) { $saveState = true; } $this->getModel()->savestate($saveState); // Display the view $this->cacheParams = $this->cacheParams ?: null; $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 * @throws Exception */ public function read(): void { // 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); 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->cacheParams = $this->cacheParams ?: null; $this->display(in_array('read', $this->cacheableTasks), $this->cacheParams); } /** * Single record add. The form layout is used to present a blank page. * * @return void * @throws Exception */ public function add(): void { // 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->cacheParams = $this->cacheParams ?: null; $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 * @throws Exception */ public function edit(): void { // Load the model /** @var DataModel $model */ $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $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->cacheParams = $this->cacheParams ?: null; $this->display(in_array('edit', $this->cacheableTasks), $this->cacheParams); } /** * Save the incoming data and then return to the Edit task * * @return void * @throws Exception */ public function apply(): void { // 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 * @throws Exception */ public function copy(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model); $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 * @throws Exception */ public function save(): void { // 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 * @throws Exception */ public function savenew(): bool { // CSRF prevention $this->csrfProtection(); if (!$this->applySave()) { return false; } $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)); return true; } /** * Save the incoming data as a copy of the given model and then redirect to the copied object edit view * * @return bool * @throws Exception */ public function save2copy(): bool { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); $ids = $this->getIDsFromRequest($model); $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(): void { $model = $this->getModel()->tmpInstance()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } 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 * @throws Exception */ public function publish(): void { // 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 * @throws Exception */ public function unpublish(): void { // 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 * @throws Exception */ public function archive(): void { // 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 * @throws Exception */ public function trash(): void { // 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 * @throws Exception */ public function checkin(): void { // 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 * @throws Exception */ public function saveorder(): void { // 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)) !== 0) { 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 * @throws Exception */ public function orderdown(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $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 * @throws Exception */ public function orderup(): void { // CSRF prevention $this->csrfProtection(); $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $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(): void { $this->deleteOrTrash(); } /** * Deletes the selected item(s). Unlike remove() this method will force delete the record (completely removed from * database) * * @return void */ public function forceRemove(): void { $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(?string $name = null, array $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 int[] */ public function getIDsFromRequest(DataModel &$model, bool $loadRecord = true): array { // 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; } elseif (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(): bool { $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(): string { 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 bool True if everything went well * * @throws Exception */ protected function onAfterExecute(string $task): bool { // 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 true; } // Not an error, avoid redirect and display the record(s) $this->redirect = false; $this->display(); return true; } 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(): string { // 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(string $area): bool { $area = $this->getACLRuleFor($area); $result = parent::checkACL($area); // 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. return $owner_id === $platform->getUser()->id; } } // No result found? Not authorised. return false; } /** * Automatically decides whether to trash or delete a record based in the $forceDelete parameter * * @param bool $forceDelete Should I delete the record? If false, the record will be trashed instead. * * @throws Exception */ protected function deleteOrTrash(bool $forceDelete = false): void { // 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(): bool { // Load the model $model = $this->getModel()->savestate(false); if (!$model->getId()) { $this->getIDsFromRequest($model); } $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'; $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'; $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); } $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 f}!]n2/!z !z Controller.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller; defined('_JEXEC') || die; use Exception; use FOF40\Container\Container; use FOF40\Controller\Exception\CannotGetName; use FOF40\Controller\Exception\TaskNotFound; use FOF40\Model\Model; use FOF40\View\Exception\AccessForbidden; use FOF40\View\View; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Cache\Cache; use Joomla\CMS\Cache\Exception\CacheExceptionInterface; use Joomla\CMS\Document\Document; use Joomla\CMS\Factory as JoomlaFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\Log\Log; use Joomla\CMS\Router\Route; use Joomla\CMS\Uri\Uri; use stdClass; /** * Class Controller * * A generic MVC controller implementation * * @property-read \FOF40\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; /** * The mapped task that was performed. * * @var string */ protected $doTask; /** * Bit mask to enable routing through Route 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|null */ 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; /** * 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; /** * Overrides the name of the view's default view * * @var string */ protected $viewName; /** * 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; /** * 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('\\FOF40\\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(string $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 mixed|bool False on execution failure * * @throws TaskNotFound When the task is not found */ public function execute(string $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 array|null $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 * * @throws Exception */ public function display(bool $cachable = false, ?array $urlparams = null, ?string $tpl = null): void { $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 Cache 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(); $groups = $user->guest ? [] : $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 = JoomlaFactory::getApplication(); $registeredurlparams = null; $registeredurlparams = !empty($app->registeredurlparams) ? $app->registeredurlparams : new stdClass; foreach ($urlparams as $key => $value) { // Add your safe url parameters with variable type as value {@see InputFilter::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 \Joomla\CMS\Cache\Controller\ViewController $cache */ $cache = JoomlaFactory::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 * * @throws Exception */ 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(?string $name = null, array $config = []) { if (!empty($name)) { $modelName = $name; } elseif (!empty($this->modelName)) { $modelName = $this->modelName; } else { $modelName = $this->view; } // Do we already have an instance of that model cached? if (array_key_exists($modelName, $this->modelInstances)) { return $this->modelInstances[$modelName]; } 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(?string $name = null, array $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(string $viewName, View &$view): void { $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(string $viewName): void { $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(string $modelName): void { $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(string $modelName, Model &$model): void { $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(): string { if (empty($this->name)) { $r = null; if (!preg_match('/(.*)\\\\Controller\\\\(.*)/i', get_class($this), $r)) { throw new CannotGetName(Text::_('LIB_FOF40_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(): string { return $this->task; } /** * Gets the available tasks in the controller. * * @return string[] Array of task names. */ public function getTasks(): array { return $this->methods; } /** * Redirects the browser or returns false if no redirect is set. * * @return boolean False if no redirect exists. */ public function redirect(): bool { if (!empty($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(string $method): Controller { $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(string $task, string $method): Controller { 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(string $task): Controller { 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|null Previous message */ public function setMessage(string $text, string $type = 'message'): ?string { $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(string $url, ?string $msg = null, ?string $type = null): Controller { // If we're parsing a non-SEF URL decide whether to use Route 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(): bool { 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(): bool { 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; // 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->showErrorPage(new AccessForbidden(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(string $event, array $arguments = []): bool { $result = true; // If there is an object method for this event, call it if (method_exists($this, $event)) { $result = $this->{$event}(...$arguments); } // If there is no handler method perform a simple ACL check elseif (substr($event, 0, 8) == 'onBefore') { $task = substr($event, 8); if (array_key_exists($task, $this->taskMap)) { $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); return !in_array(false, $results, 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(string $area): bool { $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 (strtolower($area) == 'guest') { return $this->container->platform->getUser()->guest; } if (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 string|bool The resolved ACL privilege */ protected function getACLRuleFor(string $area, array $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 f}!]^L�� � Mixin/PredefinedTaskList.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Mixin; defined('_JEXEC') || die; use FOF40\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(string $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): void { /** @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 f}!]݄�ы � Exception/NotADataModel.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use InvalidArgumentException; /** * Exception thrown when the provided Model is not a DataModel */ class NotADataModel extends InvalidArgumentException { } PK f}!]��� � Exception/TaskNotFound.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use InvalidArgumentException; /** * Exception thrown when we can't find a suitable method to handle the requested task */ class TaskNotFound extends InvalidArgumentException { } PK f}!]E�![v v Exception/CannotGetName.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use RuntimeException; /** * Exception thrown when we can't get a Controller's name */ class CannotGetName extends RuntimeException { } PK f}!]�2�� � Exception/LockedRecord.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use Exception; use Joomla\CMS\Language\Text; use RuntimeException; /** * Exception thrown when the provided Model is locked for writing by another user */ class LockedRecord extends RuntimeException { public function __construct(string $message = "", int $code = 403, Exception $previous = null) { if (empty($message)) { $message = Text::_('LIB_FOF40_CONTROLLER_ERR_LOCKED'); } parent::__construct($message, $code, $previous); } } PK f}!]���a� � Exception/ItemNotFound.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use RuntimeException; /** * Exception thrown when we can't find the requested item in a read task */ class ItemNotFound extends RuntimeException { } PK f}!]���\� � Exception/NotADataView.phpnu &1i� <?php /** * @package FOF * @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU General Public License version 3, or later */ namespace FOF40\Controller\Exception; defined('_JEXEC') || die; use InvalidArgumentException; /** * Exception thrown when the provided View does not implement DataViewInterface */ class NotADataView extends InvalidArgumentException { } PK At!]�s�+e +e FormController.phpnu &1i� PK At!]��R�Od Od me BaseController.phpnu &1i� PK At!]���"�% �% �� AdminController.phpnu &1i� PK f}!]�T;7� 7� �� DataController.phpnu &1i� PK f}!]n2/!z !z =� Controller.phpnu &1i� PK f}!]^L�� � � Mixin/PredefinedTaskList.phpnu &1i� PK f}!]݄�ы � � Exception/NotADataModel.phpnu &1i� PK f}!]��� � \ Exception/TaskNotFound.phpnu &1i� PK f}!]E�![v v G Exception/CannotGetName.phpnu &1i� PK f}!]�2�� � Exception/LockedRecord.phpnu &1i� PK f}!]���a� � � Exception/ItemNotFound.phpnu &1i� PK f}!]���\� � � Exception/NotADataView.phpnu &1i� PK �
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка