/libraries/legacy/controller/form.php
PHP | 791 lines | 444 code | 109 blank | 238 comment | 49 complexity | 26004496cb50d8ad95bc44b6ba60c2c7 MD5 | raw file
- <?php
- /**
- * @package Joomla.Legacy
- * @subpackage Controller
- *
- * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
- * @license GNU General Public License version 2 or later; see LICENSE
- */
- defined('JPATH_PLATFORM') or die;
- /**
- * Controller tailored to suit most form-based admin operations.
- *
- * @package Joomla.Legacy
- * @subpackage Controller
- * @since 12.2
- * @todo Add ability to set redirect manually to better cope with frontend usage.
- */
- class JControllerForm extends JControllerLegacy
- {
- /**
- * The context for storing internal data, e.g. record.
- *
- * @var string
- * @since 12.2
- */
- protected $context;
- /**
- * The URL option for the component.
- *
- * @var string
- * @since 12.2
- */
- protected $option;
- /**
- * The URL view item variable.
- *
- * @var string
- * @since 12.2
- */
- protected $view_item;
- /**
- * The URL view list variable.
- *
- * @var string
- * @since 12.2
- */
- protected $view_list;
- /**
- * The prefix to use with controller messages.
- *
- * @var string
- * @since 12.2
- */
- protected $text_prefix;
- /**
- * Constructor.
- *
- * @param array $config An optional associative array of configuration settings.
- *
- * @see JControllerLegacy
- * @since 12.2
- * @throws Exception
- */
- public function __construct($config = array())
- {
- parent::__construct($config);
- // 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');
- }
- /**
- * Method to add a new record.
- *
- * @return mixed True if the record can be added, a error object if not.
- *
- * @since 12.2
- */
- public function add()
- {
- $app = JFactory::getApplication();
- $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.
- $app->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 12.2
- */
- 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 add a new 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 12.2
- */
- 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 12.2
- */
- 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 12.2
- */
- public function batch($model)
- {
- $vars = $this->input->post->get('batch', array(), 'array');
- $cid = $this->input->post->get('cid', array(), 'array');
- // Build an array of item contexts to check
- $contexts = array();
- foreach ($cid as $id)
- {
- // If we're coming from com_categories, we need to use extension vs. option
- if (isset($this->extension))
- {
- $option = $this->extension;
- }
- else
- {
- $option = $this->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()));
- 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 12.2
- */
- public function cancel($key = null)
- {
- JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
- $app = JFactory::getApplication();
- $model = $this->getModel();
- $table = $model->getTable();
- $checkin = property_exists($table, 'checked_out');
- $context = "$this->option.edit.$this->context";
- if (empty($key))
- {
- $key = $table->getKeyName();
- }
- $recordId = $app->input->getInt($key);
- // Attempt to check-in the current record.
- if ($recordId)
- {
- // Check we are holding the id in the edit list.
- if (!$this->checkEditId($context, $recordId))
- {
- // Somehow the person just went to the form - we don't allow that.
- $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $recordId));
- $this->setMessage($this->getError(), 'error');
- $this->setRedirect(
- JRoute::_(
- 'index.php?option=' . $this->option . '&view=' . $this->view_list
- . $this->getRedirectToListAppend(), false
- )
- );
- return false;
- }
- if ($checkin)
- {
- if ($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);
- $app->setUserState($context . '.data', null);
- $this->setRedirect(
- JRoute::_(
- 'index.php?option=' . $this->option . '&view=' . $this->view_list
- . $this->getRedirectToListAppend(), 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 12.2
- */
- public function edit($key = null, $urlVar = null)
- {
- $app = JFactory::getApplication();
- $model = $this->getModel();
- $table = $model->getTable();
- $cid = $this->input->post->get('cid', array(), 'array');
- $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, '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);
- $app->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 object The model.
- *
- * @since 12.2
- */
- 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 12.2
- */
- protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id')
- {
- $tmpl = $this->input->get('tmpl');
- $layout = $this->input->get('layout', 'edit');
- $append = '';
- // Setup redirect info.
- if ($tmpl)
- {
- $append .= '&tmpl=' . $tmpl;
- }
- if ($layout)
- {
- $append .= '&layout=' . $layout;
- }
- if ($recordId)
- {
- $append .= '&' . $urlVar . '=' . $recordId;
- }
- return $append;
- }
- /**
- * Gets the URL arguments to append to a list redirect.
- *
- * @return string The arguments to append to the redirect URL.
- *
- * @since 12.2
- */
- protected function getRedirectToListAppend()
- {
- $tmpl = JFactory::getApplication()->input->get('tmpl');
- $append = '';
- // Setup redirect info.
- if ($tmpl)
- {
- $append .= '&tmpl=' . $tmpl;
- }
- 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 12.2
- */
- protected function postSaveHook(JModelLegacy $model, $validData = array())
- {
- }
- /**
- * 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 12.2
- */
- public function save($key = null, $urlVar = null)
- {
- // Check for request forgeries.
- JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
- $app = JFactory::getApplication();
- $lang = JFactory::getLanguage();
- $model = $this->getModel();
- $table = $model->getTable();
- $data = $this->input->post->get('jform', array(), 'array');
- $checkin = property_exists($table, '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);
- if (!$this->checkEditId($context, $recordId))
- {
- // Somehow the person just went to the form and tried to save it. We don't allow that.
- $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $recordId));
- $this->setMessage($this->getError(), 'error');
- $this->setRedirect(
- JRoute::_(
- 'index.php?option=' . $this->option . '&view=' . $this->view_list
- . $this->getRedirectToListAppend(), false
- )
- );
- return false;
- }
- // 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 and then treat the request as for Apply.
- $data[$key] = 0;
- $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;
- }
- // 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');
- }
- }
- // 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['metadata']['tags']))
- {
- $validData['metadata']['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;
- }
- $this->setMessage(
- JText::_(
- ($lang->hasKey($this->text_prefix . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS')
- ? $this->text_prefix
- : 'JLIB_APPLICATION') . ($recordId == 0 && $app->isSite() ? '_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);
- // Redirect to the list screen.
- $this->setRedirect(
- JRoute::_(
- 'index.php?option=' . $this->option . '&view=' . $this->view_list
- . $this->getRedirectToListAppend(), false
- )
- );
- break;
- }
- // Invoke the postSave method to allow for the child class to access the model.
- $this->postSaveHook($model, $validData);
- return true;
- }
- }