PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/cake/libs/controller/controller.php

https://github.com/Forbin/cakephp2x
PHP | 1266 lines | 625 code | 123 blank | 518 comment | 161 complexity | cd3361f40ef874bba88c6babfe8db43f MD5 | raw file
  1. <?php
  2. /**
  3. * Base controller class.
  4. *
  5. * PHP Version 5.x
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package cake
  16. * @subpackage cake.cake.libs.controller
  17. * @since CakePHP(tm) v 0.2.9
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. /**
  21. * Include files
  22. */
  23. App::import('Controller', 'Component', false);
  24. App::import('View', 'View', false);
  25. /**
  26. * Controller
  27. *
  28. * Application controller class for organization of business logic.
  29. * Provides basic functionality, such as rendering views inside layouts,
  30. * automatic model availability, redirection, callbacks, and more.
  31. *
  32. * @package cake
  33. * @subpackage cake.cake.libs.controller
  34. * @link http://book.cakephp.org/view/49/Controllers
  35. */
  36. class Controller extends Object {
  37. /**
  38. * The name of this controller. Controller names are plural, named after the model they manipulate.
  39. *
  40. * @var string
  41. * @access public
  42. * @link http://book.cakephp.org/view/52/name
  43. */
  44. public $name = null;
  45. /**
  46. * Stores the current URL, relative to the webroot of the application.
  47. *
  48. * @var string
  49. * @access public
  50. */
  51. public $here = null;
  52. /**
  53. * The webroot of the application.
  54. *
  55. * @var string
  56. * @access public
  57. */
  58. public $webroot = null;
  59. /**
  60. * The name of the currently requested controller action.
  61. *
  62. * @var string
  63. * @access public
  64. */
  65. public $action = null;
  66. /**
  67. * An array containing the class names of models this controller uses.
  68. *
  69. * Example: var $uses = array('Product', 'Post', 'Comment');
  70. *
  71. * Can be set to array() to use no models. Can be set to false to
  72. * use no models and prevent the merging of $uses with AppController
  73. *
  74. * @var mixed A single name as a string or a list of names as an array.
  75. * @access protected
  76. * @link http://book.cakephp.org/view/53/components-helpers-and-uses
  77. */
  78. public $uses = false;
  79. /**
  80. * An array containing the names of helpers this controller uses. The array elements should
  81. * not contain the "Helper" part of the classname.
  82. *
  83. * Example: var $helpers = array('Html', 'Javascript', 'Time', 'Ajax');
  84. *
  85. * @var mixed A single name as a string or a list of names as an array.
  86. * @access protected
  87. * @link http://book.cakephp.org/view/53/components-helpers-and-uses
  88. */
  89. public $helpers = array('Html', 'Form');
  90. /**
  91. * Parameters received in the current request: GET and POST data, information
  92. * about the request, etc.
  93. *
  94. * @var array
  95. * @access public
  96. * @link http://book.cakephp.org/view/55/The-Parameters-Attribute-params
  97. */
  98. public $params = array();
  99. /**
  100. * Data POSTed to the controller using the HtmlHelper. Data here is accessible
  101. * using the $this->data['ModelName']['fieldName'] pattern.
  102. *
  103. * @var array
  104. * @access public
  105. */
  106. public $data = array();
  107. /**
  108. * Holds pagination defaults for controller actions. The keys that can be included
  109. * in this array are: 'conditions', 'fields', 'order', 'limit', 'page', and 'recursive',
  110. * similar to the keys in the second parameter of Model::find().
  111. *
  112. * Pagination defaults can also be supplied in a model-by-model basis by using
  113. * the name of the model as a key for a pagination array:
  114. *
  115. * var $paginate = array(
  116. * 'Post' => array(...),
  117. * 'Comment' => array(...)
  118. * );
  119. *
  120. * @var array
  121. * @access public
  122. * @link http://book.cakephp.org/view/164/Pagination
  123. */
  124. public $paginate = array('limit' => 20, 'page' => 1);
  125. /**
  126. * The name of the views subfolder containing views for this controller.
  127. *
  128. * @var string
  129. * @access public
  130. */
  131. public $viewPath = null;
  132. /**
  133. * The name of the layouts subfolder containing layouts for this controller.
  134. *
  135. * @var string
  136. * @access public
  137. */
  138. public $layoutPath = null;
  139. /**
  140. * Contains variables to be handed to the view.
  141. *
  142. * @var array
  143. * @access public
  144. */
  145. public $viewVars = array();
  146. /**
  147. * Text to be used for the $title_for_layout layout variable (usually
  148. * placed inside <title> tags.)
  149. *
  150. * @var boolean
  151. * @access public
  152. * @link http://book.cakephp.org/view/54/Page-related-Attributes-layout-and-pageTitle
  153. */
  154. public $pageTitle = false;
  155. /**
  156. * An array containing the class names of the models this controller uses.
  157. *
  158. * @var array Array of model objects.
  159. * @access public
  160. */
  161. public $modelNames = array();
  162. /**
  163. * Base URL path.
  164. *
  165. * @var string
  166. * @access public
  167. */
  168. public $base = null;
  169. /**
  170. * The name of the layout file to render the view inside of. The name specified
  171. * is the filename of the layout in /app/views/layouts without the .ctp
  172. * extension.
  173. *
  174. * @var string
  175. * @access public
  176. * @link http://book.cakephp.org/view/54/Page-related-Attributes-layout-and-pageTitle
  177. */
  178. public $layout = 'default';
  179. /**
  180. * Set to true to automatically render the view
  181. * after action logic.
  182. *
  183. * @var boolean
  184. * @access public
  185. */
  186. public $autoRender = true;
  187. /**
  188. * Set to true to automatically render the layout around views.
  189. *
  190. * @var boolean
  191. * @access public
  192. */
  193. public $autoLayout = true;
  194. /**
  195. * Instance of Component used to handle callbacks.
  196. *
  197. * @var string
  198. * @access public
  199. */
  200. public $Component = null;
  201. /**
  202. * Array containing the names of components this controller uses. Component names
  203. * should not contain the "Component" portion of the classname.
  204. *
  205. * Example: var $components = array('Session', 'RequestHandler', 'Acl');
  206. *
  207. * @var array
  208. * @access public
  209. * @link http://book.cakephp.org/view/53/components-helpers-and-uses
  210. */
  211. public $components = array();
  212. /**
  213. * The name of the View class this controller sends output to.
  214. *
  215. * @var string
  216. * @access public
  217. */
  218. public $view = 'View';
  219. /**
  220. * File extension for view templates. Defaults to Cake's conventional ".ctp".
  221. *
  222. * @var string
  223. * @access public
  224. */
  225. public $ext = '.ctp';
  226. /**
  227. * The output of the requested action. Contains either a variable
  228. * returned from the action, or the data of the rendered view;
  229. * You can use this var in child controllers' afterFilter() callbacks to alter output.
  230. *
  231. * @var string
  232. * @access public
  233. */
  234. public $output = null;
  235. /**
  236. * Automatically set to the name of a plugin.
  237. *
  238. * @var string
  239. * @access public
  240. */
  241. public $plugin = null;
  242. /**
  243. * Used to define methods a controller that will be cached. To cache a
  244. * single action, the value is set to an array containing keys that match
  245. * action names and values that denote cache expiration times (in seconds).
  246. *
  247. * Example: var $cacheAction = array(
  248. * 'view/23/' => 21600,
  249. * 'recalled/' => 86400
  250. * );
  251. *
  252. * $cacheAction can also be set to a strtotime() compatible string. This
  253. * marks all the actions in the controller for view caching.
  254. *
  255. * @var mixed
  256. * @access public
  257. * @link http://book.cakephp.org/view/346/Caching-in-the-Controller
  258. */
  259. public $cacheAction = false;
  260. /**
  261. * Used to create cached instances of models a controller uses.
  262. * When set to true, all models related to the controller will be cached.
  263. * This can increase performance in many cases.
  264. *
  265. * @var boolean
  266. * @access public
  267. */
  268. public $persistModel = false;
  269. /**
  270. * Holds all params passed and named.
  271. *
  272. * @var mixed
  273. * @access public
  274. */
  275. public $passedArgs = array();
  276. /**
  277. * Triggers Scaffolding
  278. *
  279. * @var mixed
  280. * @access public
  281. * @link http://book.cakephp.org/view/105/Scaffolding
  282. */
  283. public $scaffold = false;
  284. /**
  285. * Holds current methods of the controller
  286. *
  287. * @var array
  288. * @access public
  289. * @link
  290. */
  291. public $methods = array();
  292. /**
  293. * This controller's primary model class name, the Inflector::classify()'ed version of
  294. * the controller's $name property.
  295. *
  296. * Example: For a controller named 'Comments', the modelClass would be 'Comment'
  297. *
  298. * @var string
  299. * @access public
  300. */
  301. public $modelClass = null;
  302. /**
  303. * This controller's model key name, an underscored version of the controller's $modelClass property.
  304. *
  305. * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment'
  306. *
  307. * @var string
  308. * @access public
  309. */
  310. public $modelKey = null;
  311. /**
  312. * Holds any validation errors produced by the last call of the validateErrors() method/
  313. *
  314. * @var array Validation errors, or false if none
  315. * @access public
  316. */
  317. public $validationErrors = null;
  318. /**
  319. * Contains a list of the HTTP codes that CakePHP recognizes. These may be
  320. * queried and/or modified through Controller::httpCodes(), which is also
  321. * tasked with their lazy-loading.
  322. *
  323. * @var array Associative array of HTTP codes and their associated messages.
  324. * @access private
  325. */
  326. var $__httpCodes = null;
  327. /**
  328. * Constructor.
  329. *
  330. */
  331. public function __construct() {
  332. if ($this->name === null) {
  333. $r = null;
  334. if (!preg_match('/(.*)Controller/i', get_class($this), $r)) {
  335. __("Controller::__construct() : Can not get or parse my own class name, exiting.");
  336. $this->_stop();
  337. }
  338. $this->name = $r[1];
  339. }
  340. if ($this->viewPath == null) {
  341. $this->viewPath = Inflector::underscore($this->name);
  342. }
  343. $this->modelClass = Inflector::classify($this->name);
  344. $this->modelKey = Inflector::underscore($this->modelClass);
  345. $this->Component = new Component();
  346. $childMethods = get_class_methods($this);
  347. $parentMethods = get_class_methods('Controller');
  348. foreach ($childMethods as $key => $value) {
  349. $childMethods[$key] = strtolower($value);
  350. }
  351. foreach ($parentMethods as $key => $value) {
  352. $parentMethods[$key] = strtolower($value);
  353. }
  354. $this->methods = array_diff($childMethods, $parentMethods);
  355. parent::__construct();
  356. }
  357. /**
  358. * Merge components, helpers, and uses vars from AppController and PluginAppController.
  359. *
  360. * @return void
  361. * @access protected
  362. */
  363. private function __mergeVars() {
  364. $pluginName = Inflector::camelize($this->plugin);
  365. $pluginController = $pluginName . 'AppController';
  366. if (is_subclass_of($this, 'AppController') || is_subclass_of($this, $pluginController)) {
  367. $appVars = get_class_vars('AppController');
  368. $uses = $appVars['uses'];
  369. $merge = array('components', 'helpers');
  370. $plugin = null;
  371. if (!empty($this->plugin)) {
  372. $plugin = $pluginName . '.';
  373. if (!is_subclass_of($this, $pluginController)) {
  374. $pluginController = null;
  375. }
  376. } else {
  377. $pluginController = null;
  378. }
  379. if ($uses == $this->uses && !empty($this->uses)) {
  380. if (!in_array($plugin . $this->modelClass, $this->uses)) {
  381. array_unshift($this->uses, $plugin . $this->modelClass);
  382. } elseif ($this->uses[0] !== $plugin . $this->modelClass) {
  383. $this->uses = array_flip($this->uses);
  384. unset($this->uses[$plugin . $this->modelClass]);
  385. $this->uses = array_flip($this->uses);
  386. array_unshift($this->uses, $plugin . $this->modelClass);
  387. }
  388. } elseif ($this->uses !== null || $this->uses !== false) {
  389. $merge[] = 'uses';
  390. }
  391. foreach ($merge as $var) {
  392. if (!empty($appVars[$var]) && is_array($this->{$var})) {
  393. if ($var === 'components') {
  394. $normal = Set::normalize($this->{$var});
  395. $app = Set::normalize($appVars[$var]);
  396. if ($app !== $normal) {
  397. $this->{$var} = Set::merge($app, $normal);
  398. }
  399. } else {
  400. $this->{$var} = Set::merge(
  401. $this->{$var}, array_diff($appVars[$var], $this->{$var})
  402. );
  403. }
  404. }
  405. }
  406. }
  407. if ($pluginController && $pluginName != null) {
  408. $appVars = get_class_vars($pluginController);
  409. $uses = $appVars['uses'];
  410. $merge = array('components', 'helpers');
  411. if ($this->uses !== null || $this->uses !== false) {
  412. $merge[] = 'uses';
  413. }
  414. foreach ($merge as $var) {
  415. if (isset($appVars[$var]) && !empty($appVars[$var]) && is_array($this->{$var})) {
  416. if ($var === 'components') {
  417. $normal = Set::normalize($this->{$var});
  418. $app = Set::normalize($appVars[$var]);
  419. if ($app !== $normal) {
  420. $this->{$var} = Set::merge($app, $normal);
  421. }
  422. } else {
  423. $this->{$var} = Set::merge(
  424. $this->{$var}, array_diff($appVars[$var], $this->{$var})
  425. );
  426. }
  427. }
  428. }
  429. }
  430. }
  431. /**
  432. * Loads Model classes based on the the uses property
  433. * see Controller::loadModel(); for more info.
  434. * Loads Components and prepares them for initialization.
  435. *
  436. * @return mixed true if models found and instance created, or cakeError if models not found.
  437. * @access public
  438. * @see Controller::loadModel()
  439. * @link http://book.cakephp.org/view/429/constructClasses
  440. */
  441. public function constructClasses() {
  442. $this->__mergeVars();
  443. $this->Component->init($this);
  444. if ($this->uses !== null || ($this->uses !== array())) {
  445. if (empty($this->passedArgs) || !isset($this->passedArgs['0'])) {
  446. $id = false;
  447. } else {
  448. $id = $this->passedArgs['0'];
  449. }
  450. if ($this->uses === false) {
  451. $this->loadModel($this->modelClass, $id);
  452. } elseif ($this->uses) {
  453. $uses = is_array($this->uses) ? $this->uses : array($this->uses);
  454. $modelClassName = $uses[0];
  455. if (strpos($uses[0], '.') !== false) {
  456. list($plugin, $modelClassName) = explode('.', $uses[0]);
  457. }
  458. $this->modelClass = $modelClassName;
  459. foreach ($uses as $modelClass) {
  460. $this->loadModel($modelClass);
  461. }
  462. }
  463. }
  464. return true;
  465. }
  466. /**
  467. * Queries & sets valid HTTP response codes & messages.
  468. *
  469. * @param mixed $code If $code is an integer, then the corresponding code/message is
  470. * returned if it exists, null if it does not exist. If $code is an array,
  471. * then the 'code' and 'message' keys of each nested array are added to the default
  472. * HTTP codes. Example:
  473. *
  474. * httpCodes(404); // returns array(404 => 'Not Found')
  475. *
  476. * httpCodes(array(
  477. * 701 => 'Unicorn Moved',
  478. * 800 => 'Unexpected Minotaur'
  479. * )); // sets these new values, and returns true
  480. *
  481. * @return mixed Associative array of the HTTP codes as keys, and the message
  482. * strings as values, or null of the given $code does not exist.
  483. */
  484. function httpCodes($code = null) {
  485. if (empty($this->__httpCodes)) {
  486. $this->__httpCodes = array(
  487. 100 => 'Continue', 101 => 'Switching Protocols',
  488. 200 => 'OK', 201 => 'Created', 202 => 'Accepted',
  489. 203 => 'Non-Authoritative Information', 204 => 'No Content',
  490. 205 => 'Reset Content', 206 => 'Partial Content',
  491. 300 => 'Multiple Choices', 301 => 'Moved Permanently',
  492. 302 => 'Found', 303 => 'See Other',
  493. 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
  494. 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required',
  495. 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed',
  496. 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required',
  497. 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone',
  498. 411 => 'Length Required', 412 => 'Precondition Failed',
  499. 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large',
  500. 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable',
  501. 417 => 'Expectation Failed', 500 => 'Internal Server Error',
  502. 501 => 'Not Implemented', 502 => 'Bad Gateway',
  503. 503 => 'Service Unavailable', 504 => 'Gateway Time-out'
  504. );
  505. }
  506. if (empty($code)) {
  507. return $this->__httpCodes;
  508. }
  509. if (is_array($code)) {
  510. $this->__httpCodes = $code + $this->__httpCodes;
  511. return true;
  512. }
  513. if (!isset($this->__httpCodes[$code])) {
  514. return null;
  515. }
  516. return array($code => $this->__httpCodes[$code]);
  517. }
  518. /**
  519. * Loads and instantiates models required by this controller.
  520. * If Controller::persistModel; is true, controller will cache model instances on first request,
  521. * additional request will used cached models.
  522. * If the model is non existent, it will throw a missing database table error, as Cake generates
  523. * dynamic models for the time being.
  524. *
  525. * @param string $modelClass Name of model class to load
  526. * @param mixed $id Initial ID the instanced model class should have
  527. * @return mixed true when single model found and instance created error returned if models not found.
  528. * @access public
  529. */
  530. public function loadModel($modelClass = null, $id = null) {
  531. if ($modelClass === null) {
  532. $modelClass = $this->modelClass;
  533. }
  534. $cached = false;
  535. $object = null;
  536. $plugin = null;
  537. if ($this->uses === false) {
  538. if ($this->plugin) {
  539. $plugin = $this->plugin . '.';
  540. }
  541. }
  542. list($plugin, $modelClass) = pluginSplit($modelClass, true, $plugin);
  543. if ($this->persistModel === true) {
  544. $cached = $this->_persist($modelClass, null, $object);
  545. }
  546. if (($cached === false)) {
  547. $this->modelNames[] = $modelClass;
  548. $this->{$modelClass} = ClassRegistry::init(array(
  549. 'class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id));
  550. if (!$this->{$modelClass}) {
  551. return $this->cakeError('missingModel', array(array(
  552. 'className' => $modelClass, 'webroot' => '', 'base' => $this->base
  553. )));
  554. }
  555. if ($this->persistModel === true) {
  556. $this->_persist($modelClass, true, $this->{$modelClass});
  557. $this->_persist($modelClass . 'registry', true, ClassRegistry::objects(), 'registry');
  558. }
  559. } else {
  560. $this->_persist($modelClass . 'registry', true, $object, 'registry');
  561. $this->_persist($modelClass, true, $object);
  562. $this->modelNames[] = $modelClass;
  563. }
  564. }
  565. /**
  566. * Redirects to given $url, after turning off $this->autoRender.
  567. * Script execution is halted after the redirect.
  568. *
  569. * @param mixed $url A string or array-based URL pointing to another location within the app,
  570. * or an absolute URL
  571. * @param integer $status Optional HTTP status code (eg: 404)
  572. * @param boolean $exit If true, exit() will be called after the redirect
  573. * @return mixed void if $exit = false. Terminates script if $exit = true
  574. * @access public
  575. * @link http://book.cakephp.org/view/425/redirect
  576. */
  577. public function redirect($url, $status = null, $exit = true) {
  578. $this->autoRender = false;
  579. if (is_array($status)) {
  580. extract($status, EXTR_OVERWRITE);
  581. }
  582. $response = $this->Component->beforeRedirect($this, $url, $status, $exit);
  583. if ($response === false) {
  584. return;
  585. }
  586. if (is_array($response)) {
  587. foreach ($response as $resp) {
  588. if (is_array($resp) && isset($resp['url'])) {
  589. extract($resp, EXTR_OVERWRITE);
  590. } elseif ($resp !== null) {
  591. $url = $resp;
  592. }
  593. }
  594. }
  595. if (function_exists('session_write_close')) {
  596. session_write_close();
  597. }
  598. if (!empty($status)) {
  599. $codes = $this->httpCodes();
  600. if (is_string($status)) {
  601. $codes = array_flip($codes);
  602. }
  603. if (isset($codes[$status])) {
  604. $code = $msg = $codes[$status];
  605. if (is_numeric($status)) {
  606. $code = $status;
  607. }
  608. if (is_string($status)) {
  609. $msg = $status;
  610. }
  611. $status = "HTTP/1.1 {$code} {$msg}";
  612. } else {
  613. $status = null;
  614. }
  615. $this->header($status);
  616. }
  617. if ($url !== null) {
  618. $this->header('Location: ' . Router::url($url, true));
  619. }
  620. if (!empty($status) && ($status >= 300 && $status < 400)) {
  621. $this->header($status);
  622. }
  623. if ($exit) {
  624. $this->_stop();
  625. }
  626. }
  627. /**
  628. * Convenience method for header()
  629. *
  630. * @param string $status
  631. * @return void
  632. * @access public
  633. */
  634. public function header($status) {
  635. header($status);
  636. }
  637. /**
  638. * Saves a variable for use inside a view template.
  639. *
  640. * @param mixed $one A string or an array of data.
  641. * @param mixed $two Value in case $one is a string (which then works as the key).
  642. * Unused if $one is an associative array, otherwise serves as the values to $one's keys.
  643. * @return void
  644. * @access public
  645. * @link http://book.cakephp.org/view/427/set
  646. */
  647. public function set($one, $two = null) {
  648. $data = array();
  649. if (is_array($one)) {
  650. if (is_array($two)) {
  651. $data = array_combine($one, $two);
  652. } else {
  653. $data = $one;
  654. }
  655. } else {
  656. $data = array($one => $two);
  657. }
  658. $this->viewVars = array_merge($this->viewVars, $data);
  659. }
  660. /**
  661. * Internally redirects one action to another. Examples:
  662. *
  663. * setAction('another_action');
  664. * setAction('action_with_parameters', $parameter1);
  665. *
  666. * @param string $action The new action to be redirected to
  667. * @param mixed Any other parameters passed to this method will be passed as
  668. * parameters to the new action.
  669. * @return mixed Returns the return value of the called action
  670. * @access public
  671. */
  672. public function setAction($action) {
  673. $this->action = $action;
  674. $args = func_get_args();
  675. unset($args[0]);
  676. return call_user_func_array(array(&$this, $action), $args);
  677. }
  678. /**
  679. * Controller callback to tie into Auth component.
  680. * Only called when AuthComponent::authorize is set to 'controller'.
  681. *
  682. * @return bool true if authorized, false otherwise
  683. * @access public
  684. * @link http://book.cakephp.org/view/396/authorize
  685. */
  686. public function isAuthorized() {
  687. trigger_error(sprintf(
  688. __('%s::isAuthorized() is not defined.'), $this->name
  689. ), E_USER_WARNING);
  690. return false;
  691. }
  692. /**
  693. * Returns number of errors in a submitted FORM.
  694. *
  695. * @return integer Number of errors
  696. * @access public
  697. */
  698. public function validate() {
  699. $args = func_get_args();
  700. $errors = call_user_func_array(array(&$this, 'validateErrors'), $args);
  701. if ($errors === false) {
  702. return 0;
  703. }
  704. return count($errors);
  705. }
  706. /**
  707. * Validates models passed by parameters. Example:
  708. *
  709. * $errors = $this->validateErrors($this->Article, $this->User);
  710. *
  711. * @param mixed A list of models as a variable argument
  712. * @return array Validation errors, or false if none
  713. * @access public
  714. */
  715. public function validateErrors() {
  716. $objects = func_get_args();
  717. if (empty($objects)) {
  718. return false;
  719. }
  720. $errors = array();
  721. foreach ($objects as $object) {
  722. $this->{$object->alias}->set($object->data);
  723. $errors = array_merge($errors, $this->{$object->alias}->invalidFields());
  724. }
  725. return $this->validationErrors = (!empty($errors) ? $errors : false);
  726. }
  727. /**
  728. * Instantiates the correct view class, hands it its data, and uses it to render the view output.
  729. *
  730. * @param string $action Action name to render
  731. * @param string $layout Layout to use
  732. * @param string $file File to use for rendering
  733. * @return string Full output string of view contents
  734. * @access public
  735. * @link http://book.cakephp.org/view/428/render
  736. */
  737. public function render($action = null, $layout = null, $file = null) {
  738. $this->beforeRender();
  739. $viewClass = $this->view;
  740. if ($this->view != 'View') {
  741. list($plugin, $viewClass) = pluginSplit($viewClass);
  742. $viewClass = $viewClass . 'View';
  743. App::import('View', $this->view);
  744. }
  745. $this->Component->beforeRender($this);
  746. $this->params['models'] = $this->modelNames;
  747. if (Configure::read() > 2) {
  748. $this->set('cakeDebug', $this);
  749. }
  750. $View = new $viewClass($this);
  751. if (!empty($this->modelNames)) {
  752. $models = array();
  753. foreach ($this->modelNames as $currentModel) {
  754. if (isset($this->$currentModel) && is_a($this->$currentModel, 'Model')) {
  755. $models[] = Inflector::underscore($currentModel);
  756. }
  757. $isValidModel = (
  758. isset($this->$currentModel) && is_a($this->$currentModel, 'Model') &&
  759. !empty($this->$currentModel->validationErrors)
  760. );
  761. if ($isValidModel) {
  762. $View->validationErrors[Inflector::camelize($currentModel)] = $this->$currentModel->validationErrors;
  763. }
  764. }
  765. $models = array_diff(ClassRegistry::keys(), $models);
  766. foreach ($models as $currentModel) {
  767. if (ClassRegistry::isKeySet($currentModel)) {
  768. $currentObject = ClassRegistry::getObject($currentModel);
  769. if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) {
  770. $View->validationErrors[Inflector::camelize($currentModel)] = $currentObject->validationErrors;
  771. }
  772. }
  773. }
  774. }
  775. $this->autoRender = false;
  776. $this->output .= $View->render($action, $layout, $file);
  777. return $this->output;
  778. }
  779. /**
  780. * Returns the referring URL for this request.
  781. *
  782. * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers
  783. * @param boolean $local If true, restrict referring URLs to local server
  784. * @return string Referring URL
  785. * @access public
  786. * @link http://book.cakephp.org/view/430/referer
  787. */
  788. public function referer($default = null, $local = false) {
  789. $ref = env('HTTP_REFERER');
  790. if (!empty($ref) && defined('FULL_BASE_URL')) {
  791. $base = FULL_BASE_URL . $this->webroot;
  792. if (strpos($ref, $base) === 0) {
  793. $return = substr($ref, strlen($base));
  794. if ($return[0] != '/') {
  795. $return = '/'.$return;
  796. }
  797. return $return;
  798. } elseif (!$local) {
  799. return $ref;
  800. }
  801. }
  802. if ($default != null) {
  803. $url = Router::url($default, true);
  804. return $url;
  805. }
  806. return '/';
  807. }
  808. /**
  809. * Forces the user's browser not to cache the results of the current request.
  810. *
  811. * @return void
  812. * @access public
  813. * @link http://book.cakephp.org/view/431/disableCache
  814. */
  815. public function disableCache() {
  816. header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
  817. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
  818. header("Cache-Control: no-store, no-cache, must-revalidate");
  819. header("Cache-Control: post-check=0, pre-check=0", false);
  820. header("Pragma: no-cache");
  821. }
  822. /**
  823. * Shows a message to the user for $pause seconds, then redirects to $url.
  824. * Uses flash.ctp as the default layout for the message.
  825. * Does not work if the current debug level is higher than 0.
  826. *
  827. * @param string $message Message to display to the user
  828. * @param mixed $url Relative string or array-based URL to redirect to after the time expires
  829. * @param integer $pause Time to show the message
  830. * @param string $layout Layout you want to use, defaults to 'flash'
  831. * @return void Renders flash layout
  832. * @access public
  833. * @link http://book.cakephp.org/view/426/flash
  834. */
  835. function flash($message, $url, $pause = 1, $layout = 'flash') {
  836. $this->autoRender = false;
  837. $this->set('url', Router::url($url));
  838. $this->set('message', $message);
  839. $this->set('pause', $pause);
  840. $this->set('page_title', $message);
  841. $this->render(false, $layout);
  842. }
  843. /**
  844. * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call.
  845. *
  846. * @param array $data POST'ed data organized by model and field
  847. * @param mixed $op A string containing an SQL comparison operator, or an array matching operators
  848. * to fields
  849. * @param string $bool SQL boolean operator: AND, OR, XOR, etc.
  850. * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be
  851. * included in the returned conditions
  852. * @return array An array of model conditions
  853. * @access public
  854. * @link http://book.cakephp.org/view/432/postConditions
  855. */
  856. public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
  857. if (!is_array($data) || empty($data)) {
  858. if (!empty($this->data)) {
  859. $data = $this->data;
  860. } else {
  861. return null;
  862. }
  863. }
  864. $cond = array();
  865. if ($op === null) {
  866. $op = '';
  867. }
  868. $arrayOp = is_array($op);
  869. foreach ($data as $model => $fields) {
  870. foreach ($fields as $field => $value) {
  871. $key = $model.'.'.$field;
  872. $fieldOp = $op;
  873. if ($arrayOp) {
  874. if (array_key_exists($key, $op)) {
  875. $fieldOp = $op[$key];
  876. } elseif (array_key_exists($field, $op)) {
  877. $fieldOp = $op[$field];
  878. } else {
  879. $fieldOp = false;
  880. }
  881. }
  882. if ($exclusive && $fieldOp === false) {
  883. continue;
  884. }
  885. $fieldOp = strtoupper(trim($fieldOp));
  886. if ($fieldOp === 'LIKE') {
  887. $key = $key.' LIKE';
  888. $value = '%'.$value.'%';
  889. } elseif ($fieldOp && $fieldOp != '=') {
  890. $key = $key.' '.$fieldOp;
  891. }
  892. $cond[$key] = $value;
  893. }
  894. }
  895. if ($bool != null && strtoupper($bool) != 'AND') {
  896. $cond = array($bool => $cond);
  897. }
  898. return $cond;
  899. }
  900. /**
  901. * Handles automatic pagination of model records.
  902. *
  903. * @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
  904. * @param mixed $scope Conditions to use while paginating
  905. * @param array $whitelist List of allowed options for paging
  906. * @return array Model query results
  907. * @access public
  908. * @link http://book.cakephp.org/view/165/Controller-Setup
  909. * @todo Some of this code, maybe the whole method, should be refactored in the model
  910. */
  911. public function paginate($object = null, $scope = array(), $whitelist = array()) {
  912. if (is_array($object)) {
  913. $whitelist = $scope;
  914. $scope = $object;
  915. $object = null;
  916. }
  917. $assoc = null;
  918. if (is_string($object)) {
  919. $assoc = null;
  920. if (strpos($object, '.') !== false) {
  921. list($object, $assoc) = pluginSplit($object);
  922. }
  923. if ($assoc && isset($this->{$object}->{$assoc})) {
  924. $object = $this->{$object}->{$assoc};
  925. } elseif ($assoc && isset($this->{$this->modelClass}) && isset($this->{$this->modelClass}->{$assoc})) {
  926. $object = $this->{$this->modelClass}->{$assoc};
  927. } elseif (isset($this->{$object})) {
  928. $object = $this->{$object};
  929. } elseif (isset($this->{$this->modelClass}) && isset($this->{$this->modelClass}->{$object})) {
  930. $object = $this->{$this->modelClass}->{$object};
  931. }
  932. } elseif (empty($object) || $object === null) {
  933. if (isset($this->{$this->modelClass})) {
  934. $object =& $this->{$this->modelClass};
  935. } else {
  936. $className = null;
  937. $name = $this->uses[0];
  938. if (strpos($this->uses[0], '.') !== false) {
  939. list($name, $className) = explode('.', $this->uses[0]);
  940. }
  941. if ($className) {
  942. $object =& $this->{$className};
  943. } else {
  944. $object =& $this->{$name};
  945. }
  946. }
  947. }
  948. if (!is_object($object)) {
  949. trigger_error(sprintf(
  950. __('Controller::paginate() - can\'t find model %1$s in controller %2$sController'),
  951. $object, $this->name
  952. ), E_USER_WARNING);
  953. return array();
  954. }
  955. $options = array_merge($this->params, $this->params['url'], $this->passedArgs);
  956. if (isset($this->paginate[$object->alias])) {
  957. $defaults = $this->paginate[$object->alias];
  958. } else {
  959. $defaults = $this->paginate;
  960. }
  961. if (isset($options['show'])) {
  962. $options['limit'] = $options['show'];
  963. }
  964. if (isset($options['sort'])) {
  965. $direction = null;
  966. if (isset($options['direction'])) {
  967. $direction = strtolower($options['direction']);
  968. }
  969. if ($direction != 'asc' && $direction != 'desc') {
  970. $direction = 'asc';
  971. }
  972. $options['order'] = array($options['sort'] => $direction);
  973. }
  974. if (!empty($options['order']) && is_array($options['order'])) {
  975. $alias = $object->alias ;
  976. $key = $field = key($options['order']);
  977. if (strpos($key, '.') !== false) {
  978. list($alias, $field) = explode('.', $key);
  979. }
  980. $value = $options['order'][$key];
  981. unset($options['order'][$key]);
  982. if ($object->hasField($field)) {
  983. $options['order'][$alias . '.' . $field] = $value;
  984. } elseif ($object->hasField($field, true)) {
  985. $options['order'][$field] = $value;
  986. } elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field)) {
  987. $options['order'][$alias . '.' . $field] = $value;
  988. }
  989. }
  990. $vars = array('fields', 'order', 'limit', 'page', 'recursive');
  991. $keys = array_keys($options);
  992. $count = count($keys);
  993. for ($i = 0; $i < $count; $i++) {
  994. if (!in_array($keys[$i], $vars, true)) {
  995. unset($options[$keys[$i]]);
  996. }
  997. if (empty($whitelist) && ($keys[$i] === 'fields' || $keys[$i] === 'recursive')) {
  998. unset($options[$keys[$i]]);
  999. } elseif (!empty($whitelist) && !in_array($keys[$i], $whitelist)) {
  1000. unset($options[$keys[$i]]);
  1001. }
  1002. }
  1003. $conditions = $fields = $order = $limit = $page = $recursive = null;
  1004. if (!isset($defaults['conditions'])) {
  1005. $defaults['conditions'] = array();
  1006. }
  1007. $type = 'all';
  1008. if (isset($defaults[0])) {
  1009. $type = $defaults[0];
  1010. unset($defaults[0]);
  1011. }
  1012. $options = array_merge(array('page' => 1, 'limit' => 20), $defaults, $options);
  1013. $options['limit'] = (empty($options['limit']) || !is_numeric($options['limit'])) ? 1 : $options['limit'];
  1014. extract($options);
  1015. if (is_array($scope) && !empty($scope)) {
  1016. $conditions = array_merge($conditions, $scope);
  1017. } elseif (is_string($scope)) {
  1018. $conditions = array($conditions, $scope);
  1019. }
  1020. if ($recursive === null) {
  1021. $recursive = $object->recursive;
  1022. }
  1023. $extra = array_diff_key($defaults, compact(
  1024. 'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
  1025. ));
  1026. if ($type !== 'all') {
  1027. $extra['type'] = $type;
  1028. }
  1029. if (method_exists($object, 'paginateCount')) {
  1030. $count = $object->paginateCount($conditions, $recursive, $extra);
  1031. } else {
  1032. $parameters = compact('conditions');
  1033. if ($recursive != $object->recursive) {
  1034. $parameters['recursive'] = $recursive;
  1035. }
  1036. $count = $object->find('count', array_merge($parameters, $extra));
  1037. }
  1038. $pageCount = intval(ceil($count / $limit));
  1039. if ($page === 'last' || $page >= $pageCount) {
  1040. $options['page'] = $page = $pageCount;
  1041. } elseif (intval($page) < 1) {
  1042. $options['page'] = $page = 1;
  1043. }
  1044. $page = $options['page'] = (integer)$page;
  1045. if (method_exists($object, 'paginate')) {
  1046. $results = $object->paginate(
  1047. $conditions, $fields, $order, $limit, $page, $recursive, $extra
  1048. );
  1049. } else {
  1050. $parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
  1051. if ($recursive != $object->recursive) {
  1052. $parameters['recursive'] = $recursive;
  1053. }
  1054. $results = $object->find($type, array_merge($parameters, $extra));
  1055. }
  1056. $paging = array(
  1057. 'page' => $page,
  1058. 'current' => count($results),
  1059. 'count' => $count,
  1060. 'prevPage' => ($page > 1),
  1061. 'nextPage' => ($count > ($page * $limit)),
  1062. 'pageCount' => $pageCount,
  1063. 'defaults' => array_merge(array('limit' => 20, 'step' => 1), $defaults),
  1064. 'options' => $options
  1065. );
  1066. $this->params['paging'][$object->alias] = $paging;
  1067. if (!in_array('Paginator', $this->helpers) && !array_key_exists('Paginator', $this->helpers)) {
  1068. $this->helpers[] = 'Paginator';
  1069. }
  1070. return $results;
  1071. }
  1072. /**
  1073. * Called before the controller action.
  1074. *
  1075. * @access public
  1076. * @link http://book.cakephp.org/view/60/Callbacks
  1077. */
  1078. public function beforeFilter() {
  1079. }
  1080. /**
  1081. * Called after the controller action is run, but before the view is rendered.
  1082. *
  1083. * @access public
  1084. * @link http://book.cakephp.org/view/60/Callbacks
  1085. */
  1086. public function beforeRender() {
  1087. }
  1088. /**
  1089. * Called after the controller action is run and rendered.
  1090. *
  1091. * @access public
  1092. * @link http://book.cakephp.org/view/60/Callbacks
  1093. */
  1094. public function afterFilter() {
  1095. }
  1096. /**
  1097. * This method should be overridden in child classes.
  1098. *
  1099. * @param string $method name of method called example index, edit, etc.
  1100. * @return boolean Success
  1101. * @access protected
  1102. * @link http://book.cakephp.org/view/60/Callbacks
  1103. */
  1104. protected function _beforeScaffold($method) {
  1105. return true;
  1106. }
  1107. /**
  1108. * This method should be overridden in child classes.
  1109. *
  1110. * @param string $method name of method called either edit or update.
  1111. * @return boolean Success
  1112. * @access protected
  1113. * @link http://book.cakephp.org/view/60/Callbacks
  1114. */
  1115. protected function _afterScaffoldSave($method) {
  1116. return true;
  1117. }
  1118. /**
  1119. * This method should be overridden in child classes.
  1120. *
  1121. * @param string $method name of method called either edit or update.
  1122. * @return boolean Success
  1123. * @access protected
  1124. * @link http://book.cakephp.org/view/60/Callbacks
  1125. */
  1126. protected function _afterScaffoldSaveError($method) {
  1127. return true;
  1128. }
  1129. /**
  1130. * This method should be overridden in child classes.
  1131. * If not it will render a scaffold error.
  1132. * Method MUST return true in child classes
  1133. *
  1134. * @param string $method name of method called example index, edit, etc.
  1135. * @return boolean Success
  1136. * @access protected
  1137. * @link http://book.cakephp.org/view/60/Callbacks
  1138. */
  1139. protected function _scaffoldError($method) {
  1140. return false;
  1141. }
  1142. }
  1143. ?>