PageRenderTime 25ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/legacy/controller/form.php

https://bitbucket.org/pastor399/newcastleunifc
PHP | 791 lines | 444 code | 109 blank | 238 comment | 49 complexity | 26004496cb50d8ad95bc44b6ba60c2c7 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Joomla.Legacy
  4. * @subpackage Controller
  5. *
  6. * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. /**
  11. * Controller tailored to suit most form-based admin operations.
  12. *
  13. * @package Joomla.Legacy
  14. * @subpackage Controller
  15. * @since 12.2
  16. * @todo Add ability to set redirect manually to better cope with frontend usage.
  17. */
  18. class JControllerForm extends JControllerLegacy
  19. {
  20. /**
  21. * The context for storing internal data, e.g. record.
  22. *
  23. * @var string
  24. * @since 12.2
  25. */
  26. protected $context;
  27. /**
  28. * The URL option for the component.
  29. *
  30. * @var string
  31. * @since 12.2
  32. */
  33. protected $option;
  34. /**
  35. * The URL view item variable.
  36. *
  37. * @var string
  38. * @since 12.2
  39. */
  40. protected $view_item;
  41. /**
  42. * The URL view list variable.
  43. *
  44. * @var string
  45. * @since 12.2
  46. */
  47. protected $view_list;
  48. /**
  49. * The prefix to use with controller messages.
  50. *
  51. * @var string
  52. * @since 12.2
  53. */
  54. protected $text_prefix;
  55. /**
  56. * Constructor.
  57. *
  58. * @param array $config An optional associative array of configuration settings.
  59. *
  60. * @see JControllerLegacy
  61. * @since 12.2
  62. * @throws Exception
  63. */
  64. public function __construct($config = array())
  65. {
  66. parent::__construct($config);
  67. // Guess the option as com_NameOfController
  68. if (empty($this->option))
  69. {
  70. $this->option = 'com_' . strtolower($this->getName());
  71. }
  72. // Guess the JText message prefix. Defaults to the option.
  73. if (empty($this->text_prefix))
  74. {
  75. $this->text_prefix = strtoupper($this->option);
  76. }
  77. // Guess the context as the suffix, eg: OptionControllerContent.
  78. if (empty($this->context))
  79. {
  80. $r = null;
  81. if (!preg_match('/(.*)Controller(.*)/i', get_class($this), $r))
  82. {
  83. throw new Exception(JText::_('JLIB_APPLICATION_ERROR_CONTROLLER_GET_NAME'), 500);
  84. }
  85. $this->context = strtolower($r[2]);
  86. }
  87. // Guess the item view as the context.
  88. if (empty($this->view_item))
  89. {
  90. $this->view_item = $this->context;
  91. }
  92. // Guess the list view as the plural of the item view.
  93. if (empty($this->view_list))
  94. {
  95. // @TODO Probably worth moving to an inflector class based on
  96. // http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/
  97. // Simple pluralisation based on public domain snippet by Paul Osman
  98. // For more complex types, just manually set the variable in your class.
  99. $plural = array(
  100. array('/(x|ch|ss|sh)$/i', "$1es"),
  101. array('/([^aeiouy]|qu)y$/i', "$1ies"),
  102. array('/([^aeiouy]|qu)ies$/i', "$1y"),
  103. array('/(bu)s$/i', "$1ses"),
  104. array('/s$/i', "s"),
  105. array('/$/', "s"));
  106. // Check for matches using regular expressions
  107. foreach ($plural as $pattern)
  108. {
  109. if (preg_match($pattern[0], $this->view_item))
  110. {
  111. $this->view_list = preg_replace($pattern[0], $pattern[1], $this->view_item);
  112. break;
  113. }
  114. }
  115. }
  116. // Apply, Save & New, and Save As copy should be standard on forms.
  117. $this->registerTask('apply', 'save');
  118. $this->registerTask('save2new', 'save');
  119. $this->registerTask('save2copy', 'save');
  120. }
  121. /**
  122. * Method to add a new record.
  123. *
  124. * @return mixed True if the record can be added, a error object if not.
  125. *
  126. * @since 12.2
  127. */
  128. public function add()
  129. {
  130. $app = JFactory::getApplication();
  131. $context = "$this->option.edit.$this->context";
  132. // Access check.
  133. if (!$this->allowAdd())
  134. {
  135. // Set the internal error and also the redirect error.
  136. $this->setError(JText::_('JLIB_APPLICATION_ERROR_CREATE_RECORD_NOT_PERMITTED'));
  137. $this->setMessage($this->getError(), 'error');
  138. $this->setRedirect(
  139. JRoute::_(
  140. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  141. . $this->getRedirectToListAppend(), false
  142. )
  143. );
  144. return false;
  145. }
  146. // Clear the record edit information from the session.
  147. $app->setUserState($context . '.data', null);
  148. // Redirect to the edit screen.
  149. $this->setRedirect(
  150. JRoute::_(
  151. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  152. . $this->getRedirectToItemAppend(), false
  153. )
  154. );
  155. return true;
  156. }
  157. /**
  158. * Method to check if you can add a new record.
  159. *
  160. * Extended classes can override this if necessary.
  161. *
  162. * @param array $data An array of input data.
  163. *
  164. * @return boolean
  165. *
  166. * @since 12.2
  167. */
  168. protected function allowAdd($data = array())
  169. {
  170. $user = JFactory::getUser();
  171. return ($user->authorise('core.create', $this->option) || count($user->getAuthorisedCategories($this->option, 'core.create')));
  172. }
  173. /**
  174. * Method to check if you can add a new record.
  175. *
  176. * Extended classes can override this if necessary.
  177. *
  178. * @param array $data An array of input data.
  179. * @param string $key The name of the key for the primary key; default is id.
  180. *
  181. * @return boolean
  182. *
  183. * @since 12.2
  184. */
  185. protected function allowEdit($data = array(), $key = 'id')
  186. {
  187. return JFactory::getUser()->authorise('core.edit', $this->option);
  188. }
  189. /**
  190. * Method to check if you can save a new or existing record.
  191. *
  192. * Extended classes can override this if necessary.
  193. *
  194. * @param array $data An array of input data.
  195. * @param string $key The name of the key for the primary key.
  196. *
  197. * @return boolean
  198. *
  199. * @since 12.2
  200. */
  201. protected function allowSave($data, $key = 'id')
  202. {
  203. $recordId = isset($data[$key]) ? $data[$key] : '0';
  204. if ($recordId)
  205. {
  206. return $this->allowEdit($data, $key);
  207. }
  208. else
  209. {
  210. return $this->allowAdd($data);
  211. }
  212. }
  213. /**
  214. * Method to run batch operations.
  215. *
  216. * @param JModelLegacy $model The model of the component being processed.
  217. *
  218. * @return boolean True if successful, false otherwise and internal error is set.
  219. *
  220. * @since 12.2
  221. */
  222. public function batch($model)
  223. {
  224. $vars = $this->input->post->get('batch', array(), 'array');
  225. $cid = $this->input->post->get('cid', array(), 'array');
  226. // Build an array of item contexts to check
  227. $contexts = array();
  228. foreach ($cid as $id)
  229. {
  230. // If we're coming from com_categories, we need to use extension vs. option
  231. if (isset($this->extension))
  232. {
  233. $option = $this->extension;
  234. }
  235. else
  236. {
  237. $option = $this->option;
  238. }
  239. $contexts[$id] = $option . '.' . $this->context . '.' . $id;
  240. }
  241. // Attempt to run the batch operation.
  242. if ($model->batch($vars, $cid, $contexts))
  243. {
  244. $this->setMessage(JText::_('JLIB_APPLICATION_SUCCESS_BATCH'));
  245. return true;
  246. }
  247. else
  248. {
  249. $this->setMessage(JText::sprintf('JLIB_APPLICATION_ERROR_BATCH_FAILED', $model->getError()));
  250. return false;
  251. }
  252. }
  253. /**
  254. * Method to cancel an edit.
  255. *
  256. * @param string $key The name of the primary key of the URL variable.
  257. *
  258. * @return boolean True if access level checks pass, false otherwise.
  259. *
  260. * @since 12.2
  261. */
  262. public function cancel($key = null)
  263. {
  264. JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
  265. $app = JFactory::getApplication();
  266. $model = $this->getModel();
  267. $table = $model->getTable();
  268. $checkin = property_exists($table, 'checked_out');
  269. $context = "$this->option.edit.$this->context";
  270. if (empty($key))
  271. {
  272. $key = $table->getKeyName();
  273. }
  274. $recordId = $app->input->getInt($key);
  275. // Attempt to check-in the current record.
  276. if ($recordId)
  277. {
  278. // Check we are holding the id in the edit list.
  279. if (!$this->checkEditId($context, $recordId))
  280. {
  281. // Somehow the person just went to the form - we don't allow that.
  282. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $recordId));
  283. $this->setMessage($this->getError(), 'error');
  284. $this->setRedirect(
  285. JRoute::_(
  286. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  287. . $this->getRedirectToListAppend(), false
  288. )
  289. );
  290. return false;
  291. }
  292. if ($checkin)
  293. {
  294. if ($model->checkin($recordId) === false)
  295. {
  296. // Check-in failed, go back to the record and display a notice.
  297. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
  298. $this->setMessage($this->getError(), 'error');
  299. $this->setRedirect(
  300. JRoute::_(
  301. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  302. . $this->getRedirectToItemAppend($recordId, $key), false
  303. )
  304. );
  305. return false;
  306. }
  307. }
  308. }
  309. // Clean the session data and redirect.
  310. $this->releaseEditId($context, $recordId);
  311. $app->setUserState($context . '.data', null);
  312. $this->setRedirect(
  313. JRoute::_(
  314. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  315. . $this->getRedirectToListAppend(), false
  316. )
  317. );
  318. return true;
  319. }
  320. /**
  321. * Method to edit an existing record.
  322. *
  323. * @param string $key The name of the primary key of the URL variable.
  324. * @param string $urlVar The name of the URL variable if different from the primary key
  325. * (sometimes required to avoid router collisions).
  326. *
  327. * @return boolean True if access level check and checkout passes, false otherwise.
  328. *
  329. * @since 12.2
  330. */
  331. public function edit($key = null, $urlVar = null)
  332. {
  333. $app = JFactory::getApplication();
  334. $model = $this->getModel();
  335. $table = $model->getTable();
  336. $cid = $this->input->post->get('cid', array(), 'array');
  337. $context = "$this->option.edit.$this->context";
  338. // Determine the name of the primary key for the data.
  339. if (empty($key))
  340. {
  341. $key = $table->getKeyName();
  342. }
  343. // To avoid data collisions the urlVar may be different from the primary key.
  344. if (empty($urlVar))
  345. {
  346. $urlVar = $key;
  347. }
  348. // Get the previous record id (if any) and the current record id.
  349. $recordId = (int) (count($cid) ? $cid[0] : $this->input->getInt($urlVar));
  350. $checkin = property_exists($table, 'checked_out');
  351. // Access check.
  352. if (!$this->allowEdit(array($key => $recordId), $key))
  353. {
  354. $this->setError(JText::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'));
  355. $this->setMessage($this->getError(), 'error');
  356. $this->setRedirect(
  357. JRoute::_(
  358. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  359. . $this->getRedirectToListAppend(), false
  360. )
  361. );
  362. return false;
  363. }
  364. // Attempt to check-out the new record for editing and redirect.
  365. if ($checkin && !$model->checkout($recordId))
  366. {
  367. // Check-out failed, display a notice but allow the user to see the record.
  368. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKOUT_FAILED', $model->getError()));
  369. $this->setMessage($this->getError(), 'error');
  370. $this->setRedirect(
  371. JRoute::_(
  372. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  373. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  374. )
  375. );
  376. return false;
  377. }
  378. else
  379. {
  380. // Check-out succeeded, push the new record id into the session.
  381. $this->holdEditId($context, $recordId);
  382. $app->setUserState($context . '.data', null);
  383. $this->setRedirect(
  384. JRoute::_(
  385. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  386. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  387. )
  388. );
  389. return true;
  390. }
  391. }
  392. /**
  393. * Method to get a model object, loading it if required.
  394. *
  395. * @param string $name The model name. Optional.
  396. * @param string $prefix The class prefix. Optional.
  397. * @param array $config Configuration array for model. Optional.
  398. *
  399. * @return object The model.
  400. *
  401. * @since 12.2
  402. */
  403. public function getModel($name = '', $prefix = '', $config = array('ignore_request' => true))
  404. {
  405. if (empty($name))
  406. {
  407. $name = $this->context;
  408. }
  409. return parent::getModel($name, $prefix, $config);
  410. }
  411. /**
  412. * Gets the URL arguments to append to an item redirect.
  413. *
  414. * @param integer $recordId The primary key id for the item.
  415. * @param string $urlVar The name of the URL variable for the id.
  416. *
  417. * @return string The arguments to append to the redirect URL.
  418. *
  419. * @since 12.2
  420. */
  421. protected function getRedirectToItemAppend($recordId = null, $urlVar = 'id')
  422. {
  423. $tmpl = $this->input->get('tmpl');
  424. $layout = $this->input->get('layout', 'edit');
  425. $append = '';
  426. // Setup redirect info.
  427. if ($tmpl)
  428. {
  429. $append .= '&tmpl=' . $tmpl;
  430. }
  431. if ($layout)
  432. {
  433. $append .= '&layout=' . $layout;
  434. }
  435. if ($recordId)
  436. {
  437. $append .= '&' . $urlVar . '=' . $recordId;
  438. }
  439. return $append;
  440. }
  441. /**
  442. * Gets the URL arguments to append to a list redirect.
  443. *
  444. * @return string The arguments to append to the redirect URL.
  445. *
  446. * @since 12.2
  447. */
  448. protected function getRedirectToListAppend()
  449. {
  450. $tmpl = JFactory::getApplication()->input->get('tmpl');
  451. $append = '';
  452. // Setup redirect info.
  453. if ($tmpl)
  454. {
  455. $append .= '&tmpl=' . $tmpl;
  456. }
  457. return $append;
  458. }
  459. /**
  460. * Function that allows child controller access to model data
  461. * after the data has been saved.
  462. *
  463. * @param JModelLegacy $model The data model object.
  464. * @param array $validData The validated data.
  465. *
  466. * @return void
  467. *
  468. * @since 12.2
  469. */
  470. protected function postSaveHook(JModelLegacy $model, $validData = array())
  471. {
  472. }
  473. /**
  474. * Method to save a record.
  475. *
  476. * @param string $key The name of the primary key of the URL variable.
  477. * @param string $urlVar The name of the URL variable if different from the primary key (sometimes required to avoid router collisions).
  478. *
  479. * @return boolean True if successful, false otherwise.
  480. *
  481. * @since 12.2
  482. */
  483. public function save($key = null, $urlVar = null)
  484. {
  485. // Check for request forgeries.
  486. JSession::checkToken() or jexit(JText::_('JINVALID_TOKEN'));
  487. $app = JFactory::getApplication();
  488. $lang = JFactory::getLanguage();
  489. $model = $this->getModel();
  490. $table = $model->getTable();
  491. $data = $this->input->post->get('jform', array(), 'array');
  492. $checkin = property_exists($table, 'checked_out');
  493. $context = "$this->option.edit.$this->context";
  494. $task = $this->getTask();
  495. // Determine the name of the primary key for the data.
  496. if (empty($key))
  497. {
  498. $key = $table->getKeyName();
  499. }
  500. // To avoid data collisions the urlVar may be different from the primary key.
  501. if (empty($urlVar))
  502. {
  503. $urlVar = $key;
  504. }
  505. $recordId = $this->input->getInt($urlVar);
  506. if (!$this->checkEditId($context, $recordId))
  507. {
  508. // Somehow the person just went to the form and tried to save it. We don't allow that.
  509. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_UNHELD_ID', $recordId));
  510. $this->setMessage($this->getError(), 'error');
  511. $this->setRedirect(
  512. JRoute::_(
  513. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  514. . $this->getRedirectToListAppend(), false
  515. )
  516. );
  517. return false;
  518. }
  519. // Populate the row id from the session.
  520. $data[$key] = $recordId;
  521. // The save2copy task needs to be handled slightly differently.
  522. if ($task == 'save2copy')
  523. {
  524. // Check-in the original row.
  525. if ($checkin && $model->checkin($data[$key]) === false)
  526. {
  527. // Check-in failed. Go back to the item and display a notice.
  528. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
  529. $this->setMessage($this->getError(), 'error');
  530. $this->setRedirect(
  531. JRoute::_(
  532. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  533. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  534. )
  535. );
  536. return false;
  537. }
  538. // Reset the ID and then treat the request as for Apply.
  539. $data[$key] = 0;
  540. $task = 'apply';
  541. }
  542. // Access check.
  543. if (!$this->allowSave($data, $key))
  544. {
  545. $this->setError(JText::_('JLIB_APPLICATION_ERROR_SAVE_NOT_PERMITTED'));
  546. $this->setMessage($this->getError(), 'error');
  547. $this->setRedirect(
  548. JRoute::_(
  549. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  550. . $this->getRedirectToListAppend(), false
  551. )
  552. );
  553. return false;
  554. }
  555. // Validate the posted data.
  556. // Sometimes the form needs some posted data, such as for plugins and modules.
  557. $form = $model->getForm($data, false);
  558. if (!$form)
  559. {
  560. $app->enqueueMessage($model->getError(), 'error');
  561. return false;
  562. }
  563. // Test whether the data is valid.
  564. $validData = $model->validate($form, $data);
  565. // Check for validation errors.
  566. if ($validData === false)
  567. {
  568. // Get the validation messages.
  569. $errors = $model->getErrors();
  570. // Push up to three validation messages out to the user.
  571. for ($i = 0, $n = count($errors); $i < $n && $i < 3; $i++)
  572. {
  573. if ($errors[$i] instanceof Exception)
  574. {
  575. $app->enqueueMessage($errors[$i]->getMessage(), 'warning');
  576. }
  577. else
  578. {
  579. $app->enqueueMessage($errors[$i], 'warning');
  580. }
  581. }
  582. // Save the data in the session.
  583. $app->setUserState($context . '.data', $data);
  584. // Redirect back to the edit screen.
  585. $this->setRedirect(
  586. JRoute::_(
  587. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  588. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  589. )
  590. );
  591. return false;
  592. }
  593. if (!isset($validData['metadata']['tags']))
  594. {
  595. $validData['metadata']['tags'] = null;
  596. }
  597. // Attempt to save the data.
  598. if (!$model->save($validData))
  599. {
  600. // Save the data in the session.
  601. $app->setUserState($context . '.data', $validData);
  602. // Redirect back to the edit screen.
  603. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()));
  604. $this->setMessage($this->getError(), 'error');
  605. $this->setRedirect(
  606. JRoute::_(
  607. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  608. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  609. )
  610. );
  611. return false;
  612. }
  613. // Save succeeded, so check-in the record.
  614. if ($checkin && $model->checkin($validData[$key]) === false)
  615. {
  616. // Save the data in the session.
  617. $app->setUserState($context . '.data', $validData);
  618. // Check-in failed, so go back to the record and display a notice.
  619. $this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
  620. $this->setMessage($this->getError(), 'error');
  621. $this->setRedirect(
  622. JRoute::_(
  623. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  624. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  625. )
  626. );
  627. return false;
  628. }
  629. $this->setMessage(
  630. JText::_(
  631. ($lang->hasKey($this->text_prefix . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS')
  632. ? $this->text_prefix
  633. : 'JLIB_APPLICATION') . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS'
  634. )
  635. );
  636. // Redirect the user and adjust session state based on the chosen task.
  637. switch ($task)
  638. {
  639. case 'apply':
  640. // Set the record data in the session.
  641. $recordId = $model->getState($this->context . '.id');
  642. $this->holdEditId($context, $recordId);
  643. $app->setUserState($context . '.data', null);
  644. $model->checkout($recordId);
  645. // Redirect back to the edit screen.
  646. $this->setRedirect(
  647. JRoute::_(
  648. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  649. . $this->getRedirectToItemAppend($recordId, $urlVar), false
  650. )
  651. );
  652. break;
  653. case 'save2new':
  654. // Clear the record id and data from the session.
  655. $this->releaseEditId($context, $recordId);
  656. $app->setUserState($context . '.data', null);
  657. // Redirect back to the edit screen.
  658. $this->setRedirect(
  659. JRoute::_(
  660. 'index.php?option=' . $this->option . '&view=' . $this->view_item
  661. . $this->getRedirectToItemAppend(null, $urlVar), false
  662. )
  663. );
  664. break;
  665. default:
  666. // Clear the record id and data from the session.
  667. $this->releaseEditId($context, $recordId);
  668. $app->setUserState($context . '.data', null);
  669. // Redirect to the list screen.
  670. $this->setRedirect(
  671. JRoute::_(
  672. 'index.php?option=' . $this->option . '&view=' . $this->view_list
  673. . $this->getRedirectToListAppend(), false
  674. )
  675. );
  676. break;
  677. }
  678. // Invoke the postSave method to allow for the child class to access the model.
  679. $this->postSaveHook($model, $validData);
  680. return true;
  681. }
  682. }