1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11: * @link http://cakephp.org CakePHP(tm) Project
12: * @package Cake.Controller
13: * @since CakePHP(tm) v 0.2.9
14: * @license http://www.opensource.org/licenses/mit-license.php MIT License
15: */
16:
17: App::uses('CakeResponse', 'Network');
18: App::uses('ClassRegistry', 'Utility');
19: App::uses('ComponentCollection', 'Controller');
20: App::uses('View', 'View');
21: App::uses('CakeEvent', 'Event');
22: App::uses('CakeEventListener', 'Event');
23: App::uses('CakeEventManager', 'Event');
24:
25: /**
26: * Application controller class for organization of business logic.
27: * Provides basic functionality, such as rendering views inside layouts,
28: * automatic model availability, redirection, callbacks, and more.
29: *
30: * Controllers should provide a number of 'action' methods. These are public methods on the controller
31: * that are not prefixed with a '_' and not part of Controller. Each action serves as an endpoint for
32: * performing a specific action on a resource or collection of resources. For example: adding or editing a new
33: * object, or listing a set of objects.
34: *
35: * You can access request parameters, using `$this->request`. The request object contains all the POST, GET and FILES
36: * that were part of the request.
37: *
38: * After performing the required actions, controllers are responsible for creating a response. This usually
39: * takes the form of a generated View, or possibly a redirection to another controller action. In either case
40: * `$this->response` allows you to manipulate all aspects of the response.
41: *
42: * Controllers are created by Dispatcher based on request parameters and routing. By default controllers and actions
43: * use conventional names. For example `/posts/index` maps to `PostsController::index()`. You can re-map URLs
44: * using Router::connect().
45: *
46: * @package Cake.Controller
47: * @property AclComponent $Acl
48: * @property AuthComponent $Auth
49: * @property CookieComponent $Cookie
50: * @property EmailComponent $Email
51: * @property PaginatorComponent $Paginator
52: * @property RequestHandlerComponent $RequestHandler
53: * @property SecurityComponent $Security
54: * @property SessionComponent $Session
55: * @property FlashComponent $Flash
56: * @link http://book.cakephp.org/2.0/en/controllers.html
57: */
58: class Controller extends Object implements CakeEventListener {
59:
60: /**
61: * The name of this controller. Controller names are plural, named after the model they manipulate.
62: *
63: * @var string
64: * @link http://book.cakephp.org/2.0/en/controllers.html#controller-attributes
65: */
66: public $name = null;
67:
68: /**
69: * An array containing the class names of models this controller uses.
70: *
71: * Example: `public $uses = array('Product', 'Post', 'Comment');`
72: *
73: * Can be set to several values to express different options:
74: *
75: * - `true` Use the default inflected model name.
76: * - `array()` Use only models defined in the parent class.
77: * - `false` Use no models at all, do not merge with parent class either.
78: * - `array('Post', 'Comment')` Use only the Post and Comment models. Models
79: * Will also be merged with the parent class.
80: *
81: * The default value is `true`.
82: *
83: * @var mixed
84: * @link http://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
85: */
86: public $uses = true;
87:
88: /**
89: * An array containing the names of helpers this controller uses. The array elements should
90: * not contain the "Helper" part of the class name.
91: *
92: * Example: `public $helpers = array('Html', 'Js', 'Time', 'Ajax');`
93: *
94: * @var mixed
95: * @link http://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
96: */
97: public $helpers = array();
98:
99: /**
100: * An instance of a CakeRequest object that contains information about the current request.
101: * This object contains all the information about a request and several methods for reading
102: * additional information about the request.
103: *
104: * @var CakeRequest
105: * @link http://book.cakephp.org/2.0/en/controllers/request-response.html#cakerequest
106: */
107: public $request;
108:
109: /**
110: * An instance of a CakeResponse object that contains information about the impending response
111: *
112: * @var CakeResponse
113: * @link http://book.cakephp.org/2.0/en/controllers/request-response.html#cakeresponse
114: */
115: public $response;
116:
117: /**
118: * The class name to use for creating the response object.
119: *
120: * @var string
121: */
122: protected $_responseClass = 'CakeResponse';
123:
124: /**
125: * The name of the views subfolder containing views for this controller.
126: *
127: * @var string
128: */
129: public $viewPath = null;
130:
131: /**
132: * The name of the layouts subfolder containing layouts for this controller.
133: *
134: * @var string
135: */
136: public $layoutPath = null;
137:
138: /**
139: * Contains variables to be handed to the view.
140: *
141: * @var array
142: */
143: public $viewVars = array();
144:
145: /**
146: * The name of the view file to render. The name specified
147: * is the filename in /app/View/<SubFolder> without the .ctp extension.
148: *
149: * @var string
150: */
151: public $view = null;
152:
153: /**
154: * The name of the layout file to render the view inside of. The name specified
155: * is the filename of the layout in /app/View/Layouts without the .ctp
156: * extension.
157: *
158: * @var string
159: */
160: public $layout = 'default';
161:
162: /**
163: * Set to true to automatically render the view
164: * after action logic.
165: *
166: * @var bool
167: */
168: public $autoRender = true;
169:
170: /**
171: * Set to true to automatically render the layout around views.
172: *
173: * @var bool
174: */
175: public $autoLayout = true;
176:
177: /**
178: * Instance of ComponentCollection used to handle callbacks.
179: *
180: * @var ComponentCollection
181: */
182: public $Components = null;
183:
184: /**
185: * Array containing the names of components this controller uses. Component names
186: * should not contain the "Component" portion of the class name.
187: *
188: * Example: `public $components = array('Session', 'RequestHandler', 'Acl');`
189: *
190: * @var array
191: * @link http://book.cakephp.org/2.0/en/controllers/components.html
192: */
193: public $components = array('Session', 'Flash');
194:
195: /**
196: * The name of the View class this controller sends output to.
197: *
198: * @var string
199: */
200: public $viewClass = 'View';
201:
202: /**
203: * Instance of the View created during rendering. Won't be set until after
204: * Controller::render() is called.
205: *
206: * @var View
207: */
208: public $View;
209:
210: /**
211: * File extension for view templates. Defaults to CakePHP's conventional ".ctp".
212: *
213: * @var string
214: */
215: public $ext = '.ctp';
216:
217: /**
218: * Automatically set to the name of a plugin.
219: *
220: * @var string
221: */
222: public $plugin = null;
223:
224: /**
225: * Used to define methods a controller that will be cached. To cache a
226: * single action, the value is set to an array containing keys that match
227: * action names and values that denote cache expiration times (in seconds).
228: *
229: * Example:
230: *
231: * ```
232: * public $cacheAction = array(
233: * 'view/23/' => 21600,
234: * 'recalled/' => 86400
235: * );
236: * ```
237: *
238: * $cacheAction can also be set to a strtotime() compatible string. This
239: * marks all the actions in the controller for view caching.
240: *
241: * @var mixed
242: * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/cache.html#additional-configuration-options
243: */
244: public $cacheAction = false;
245:
246: /**
247: * Holds all params passed and named.
248: *
249: * @var mixed
250: */
251: public $passedArgs = array();
252:
253: /**
254: * Triggers Scaffolding
255: *
256: * @var mixed
257: * @link http://book.cakephp.org/2.0/en/controllers/scaffolding.html
258: */
259: public $scaffold = false;
260:
261: /**
262: * Holds current methods of the controller. This is a list of all the methods reachable
263: * via URL. Modifying this array will allow you to change which methods can be reached.
264: *
265: * @var array
266: */
267: public $methods = array();
268:
269: /**
270: * This controller's primary model class name, the Inflector::singularize()'ed version of
271: * the controller's $name property.
272: *
273: * Example: For a controller named 'Comments', the modelClass would be 'Comment'
274: *
275: * @var string
276: */
277: public $modelClass = null;
278:
279: /**
280: * This controller's model key name, an underscored version of the controller's $modelClass property.
281: *
282: * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment'
283: *
284: * @var string
285: */
286: public $modelKey = null;
287:
288: /**
289: * Holds any validation errors produced by the last call of the validateErrors() method.
290: *
291: * @var array
292: */
293: public $validationErrors = null;
294:
295: /**
296: * The class name of the parent class you wish to merge with.
297: * Typically this is AppController, but you may wish to merge vars with a different
298: * parent class.
299: *
300: * @var string
301: */
302: protected $_mergeParent = 'AppController';
303:
304: /**
305: * Instance of the CakeEventManager this controller is using
306: * to dispatch inner events.
307: *
308: * @var CakeEventManager
309: */
310: protected $_eventManager = null;
311:
312: /**
313: * Constructor.
314: *
315: * @param CakeRequest $request Request object for this controller. Can be null for testing,
316: * but expect that features that use the request parameters will not work.
317: * @param CakeResponse $response Response object for this controller.
318: */
319: public function __construct($request = null, $response = null) {
320: if ($this->name === null) {
321: $this->name = substr(get_class($this), 0, -10);
322: }
323:
324: if (!$this->viewPath) {
325: $this->viewPath = $this->name;
326: }
327:
328: $this->modelClass = Inflector::singularize($this->name);
329: $this->modelKey = Inflector::underscore($this->modelClass);
330: $this->Components = new ComponentCollection();
331:
332: $childMethods = get_class_methods($this);
333: $parentMethods = get_class_methods('Controller');
334:
335: $this->methods = array_diff($childMethods, $parentMethods);
336:
337: if ($request instanceof CakeRequest) {
338: $this->setRequest($request);
339: }
340: if ($response instanceof CakeResponse) {
341: $this->response = $response;
342: }
343: parent::__construct();
344: }
345:
346: /**
347: * Provides backwards compatibility to avoid problems with empty and isset to alias properties.
348: * Lazy loads models using the loadModel() method if declared in $uses
349: *
350: * @param string $name Property name to check.
351: * @return bool
352: */
353: public function __isset($name) {
354: switch ($name) {
355: case 'base':
356: case 'here':
357: case 'webroot':
358: case 'data':
359: case 'action':
360: case 'params':
361: return true;
362: }
363:
364: if (is_array($this->uses)) {
365: foreach ($this->uses as $modelClass) {
366: list($plugin, $class) = pluginSplit($modelClass, true);
367: if ($name === $class) {
368: return $this->loadModel($modelClass);
369: }
370: }
371: }
372:
373: if ($name === $this->modelClass) {
374: list($plugin, $class) = pluginSplit($name, true);
375: if (!$plugin) {
376: $plugin = $this->plugin ? $this->plugin . '.' : null;
377: }
378: return $this->loadModel($plugin . $this->modelClass);
379: }
380:
381: return false;
382: }
383:
384: /**
385: * Provides backwards compatibility access to the request object properties.
386: * Also provides the params alias.
387: *
388: * @param string $name The name of the requested value
389: * @return mixed The requested value for valid variables/aliases else null
390: */
391: public function __get($name) {
392: switch ($name) {
393: case 'base':
394: case 'here':
395: case 'webroot':
396: case 'data':
397: return $this->request->{$name};
398: case 'action':
399: return isset($this->request->params['action']) ? $this->request->params['action'] : '';
400: case 'params':
401: return $this->request;
402: case 'paginate':
403: return $this->Components->load('Paginator')->settings;
404: }
405:
406: if (isset($this->{$name})) {
407: return $this->{$name};
408: }
409:
410: return null;
411: }
412:
413: /**
414: * Provides backwards compatibility access for setting values to the request object.
415: *
416: * @param string $name Property name to set.
417: * @param mixed $value Value to set.
418: * @return void
419: */
420: public function __set($name, $value) {
421: switch ($name) {
422: case 'base':
423: case 'here':
424: case 'webroot':
425: case 'data':
426: $this->request->{$name} = $value;
427: return;
428: case 'action':
429: $this->request->params['action'] = $value;
430: return;
431: case 'params':
432: $this->request->params = $value;
433: return;
434: case 'paginate':
435: $this->Components->load('Paginator')->settings = $value;
436: return;
437: }
438: $this->{$name} = $value;
439: }
440:
441: /**
442: * Sets the request objects and configures a number of controller properties
443: * based on the contents of the request. The properties that get set are
444: *
445: * - $this->request - To the $request parameter
446: * - $this->plugin - To the $request->params['plugin']
447: * - $this->view - To the $request->params['action']
448: * - $this->autoLayout - To the false if $request->params['bare']; is set.
449: * - $this->autoRender - To false if $request->params['return'] == 1
450: * - $this->passedArgs - The the combined results of params['named'] and params['pass]
451: *
452: * @param CakeRequest $request Request instance.
453: * @return void
454: */
455: public function setRequest(CakeRequest $request) {
456: $this->request = $request;
457: $this->plugin = isset($request->params['plugin']) ? Inflector::camelize($request->params['plugin']) : null;
458: $this->view = isset($request->params['action']) ? $request->params['action'] : null;
459: if (isset($request->params['pass']) && isset($request->params['named'])) {
460: $this->passedArgs = array_merge($request->params['pass'], $request->params['named']);
461: }
462:
463: if (!empty($request->params['return']) && $request->params['return'] == 1) {
464: $this->autoRender = false;
465: }
466: if (!empty($request->params['bare'])) {
467: $this->autoLayout = false;
468: }
469: }
470:
471: /**
472: * Dispatches the controller action. Checks that the action
473: * exists and isn't private.
474: *
475: * @param CakeRequest $request Request instance.
476: * @return mixed The resulting response.
477: * @throws PrivateActionException When actions are not public or prefixed by _
478: * @throws MissingActionException When actions are not defined and scaffolding is
479: * not enabled.
480: */
481: public function invokeAction(CakeRequest $request) {
482: try {
483: $method = new ReflectionMethod($this, $request->params['action']);
484:
485: if ($this->_isPrivateAction($method, $request)) {
486: throw new PrivateActionException(array(
487: 'controller' => $this->name . "Controller",
488: 'action' => $request->params['action']
489: ));
490: }
491: return $method->invokeArgs($this, $request->params['pass']);
492:
493: } catch (ReflectionException $e) {
494: if ($this->scaffold !== false) {
495: return $this->_getScaffold($request);
496: }
497: throw new MissingActionException(array(
498: 'controller' => $this->name . "Controller",
499: 'action' => $request->params['action']
500: ));
501: }
502: }
503:
504: /**
505: * Check if the request's action is marked as private, with an underscore,
506: * or if the request is attempting to directly accessing a prefixed action.
507: *
508: * @param ReflectionMethod $method The method to be invoked.
509: * @param CakeRequest $request The request to check.
510: * @return bool
511: */
512: protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request) {
513: $privateAction = (
514: $method->name[0] === '_' ||
515: !$method->isPublic() ||
516: !in_array($method->name, $this->methods)
517: );
518: $prefixes = array_map('strtolower', Router::prefixes());
519:
520: if (!$privateAction && !empty($prefixes)) {
521: if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) {
522: list($prefix) = explode('_', $request->params['action']);
523: $privateAction = in_array(strtolower($prefix), $prefixes);
524: }
525: }
526: return $privateAction;
527: }
528:
529: /**
530: * Returns a scaffold object to use for dynamically scaffolded controllers.
531: *
532: * @param CakeRequest $request Request instance.
533: * @return Scaffold
534: */
535: protected function _getScaffold(CakeRequest $request) {
536: return new Scaffold($this, $request);
537: }
538:
539: /**
540: * Merge components, helpers, and uses vars from
541: * Controller::$_mergeParent and PluginAppController.
542: *
543: * @return void
544: */
545: protected function _mergeControllerVars() {
546: $pluginController = $pluginDot = null;
547: $mergeParent = is_subclass_of($this, $this->_mergeParent);
548: $pluginVars = array();
549: $appVars = array();
550:
551: if (!empty($this->plugin)) {
552: $pluginController = $this->plugin . 'AppController';
553: if (!is_subclass_of($this, $pluginController)) {
554: $pluginController = null;
555: }
556: $pluginDot = $this->plugin . '.';
557: }
558:
559: if ($pluginController) {
560: $merge = array('components', 'helpers');
561: $this->_mergeVars($merge, $pluginController);
562: }
563:
564: if ($mergeParent || !empty($pluginController)) {
565: $appVars = get_class_vars($this->_mergeParent);
566: $merge = array('components', 'helpers');
567: $this->_mergeVars($merge, $this->_mergeParent, true);
568: }
569:
570: if ($this->uses === null) {
571: $this->uses = false;
572: }
573: if ($this->uses === true) {
574: $this->uses = array($pluginDot . $this->modelClass);
575: }
576: if (isset($appVars['uses']) && $appVars['uses'] === $this->uses) {
577: array_unshift($this->uses, $pluginDot . $this->modelClass);
578: }
579: if ($pluginController) {
580: $pluginVars = get_class_vars($pluginController);
581: }
582: if ($this->uses !== false) {
583: $this->_mergeUses($pluginVars);
584: $this->_mergeUses($appVars);
585: } else {
586: $this->uses = array();
587: $this->modelClass = '';
588: }
589: }
590:
591: /**
592: * Helper method for merging the $uses property together.
593: *
594: * Merges the elements not already in $this->uses into
595: * $this->uses.
596: *
597: * @param array $merge The data to merge in.
598: * @return void
599: */
600: protected function _mergeUses($merge) {
601: if (!isset($merge['uses'])) {
602: return;
603: }
604: if ($merge['uses'] === true) {
605: return;
606: }
607: $this->uses = array_merge(
608: $this->uses,
609: array_diff($merge['uses'], $this->uses)
610: );
611: }
612:
613: /**
614: * Returns a list of all events that will fire in the controller during its lifecycle.
615: * You can override this function to add your own listener callbacks
616: *
617: * @return array
618: */
619: public function implementedEvents() {
620: return array(
621: 'Controller.initialize' => 'beforeFilter',
622: 'Controller.beforeRender' => 'beforeRender',
623: 'Controller.beforeRedirect' => array('callable' => 'beforeRedirect', 'passParams' => true),
624: 'Controller.shutdown' => 'afterFilter'
625: );
626: }
627:
628: /**
629: * Loads Model classes based on the uses property
630: * see Controller::loadModel(); for more info.
631: * Loads Components and prepares them for initialization.
632: *
633: * @return mixed true if models found and instance created.
634: * @see Controller::loadModel()
635: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::constructClasses
636: * @throws MissingModelException
637: */
638: public function constructClasses() {
639: $this->_mergeControllerVars();
640: if ($this->uses) {
641: $this->uses = (array)$this->uses;
642: list(, $this->modelClass) = pluginSplit(reset($this->uses));
643: }
644: $this->Components->init($this);
645: return true;
646: }
647:
648: /**
649: * Returns the CakeEventManager manager instance that is handling any callbacks.
650: * You can use this instance to register any new listeners or callbacks to the
651: * controller events, or create your own events and trigger them at will.
652: *
653: * @return CakeEventManager
654: */
655: public function getEventManager() {
656: if (empty($this->_eventManager)) {
657: $this->_eventManager = new CakeEventManager();
658: $this->_eventManager->attach($this->Components);
659: $this->_eventManager->attach($this);
660: }
661: return $this->_eventManager;
662: }
663:
664: /**
665: * Perform the startup process for this controller.
666: * Fire the Components and Controller callbacks in the correct order.
667: *
668: * - Initializes components, which fires their `initialize` callback
669: * - Calls the controller `beforeFilter`.
670: * - triggers Component `startup` methods.
671: *
672: * @return void
673: * @triggers Controller.initialize $this
674: * @triggers Controller.startup $this
675: */
676: public function startupProcess() {
677: $this->getEventManager()->dispatch(new CakeEvent('Controller.initialize', $this));
678: $this->getEventManager()->dispatch(new CakeEvent('Controller.startup', $this));
679: }
680:
681: /**
682: * Perform the various shutdown processes for this controller.
683: * Fire the Components and Controller callbacks in the correct order.
684: *
685: * - triggers the component `shutdown` callback.
686: * - calls the Controller's `afterFilter` method.
687: *
688: * @return void
689: * @triggers Controller.shutdown $this
690: */
691: public function shutdownProcess() {
692: $this->getEventManager()->dispatch(new CakeEvent('Controller.shutdown', $this));
693: }
694:
695: /**
696: * Queries & sets valid HTTP response codes & messages.
697: *
698: * @param int|array $code If $code is an integer, then the corresponding code/message is
699: * returned if it exists, null if it does not exist. If $code is an array,
700: * then the 'code' and 'message' keys of each nested array are added to the default
701: * HTTP codes. Example:
702: *
703: * httpCodes(404); // returns array(404 => 'Not Found')
704: *
705: * httpCodes(array(
706: * 701 => 'Unicorn Moved',
707: * 800 => 'Unexpected Minotaur'
708: * )); // sets these new values, and returns true
709: *
710: * @return array Associative array of the HTTP codes as keys, and the message
711: * strings as values, or null of the given $code does not exist.
712: * @deprecated 3.0.0 Since 2.4. Will be removed in 3.0. Use CakeResponse::httpCodes().
713: */
714: public function httpCodes($code = null) {
715: return $this->response->httpCodes($code);
716: }
717:
718: /**
719: * Loads and instantiates models required by this controller.
720: * If the model is non existent, it will throw a missing database table error, as CakePHP generates
721: * dynamic models for the time being.
722: *
723: * @param string $modelClass Name of model class to load
724: * @param int|string $id Initial ID the instanced model class should have
725: * @return bool True if the model was found
726: * @throws MissingModelException if the model class cannot be found.
727: */
728: public function loadModel($modelClass = null, $id = null) {
729: if ($modelClass === null) {
730: $modelClass = $this->modelClass;
731: }
732:
733: $this->uses = ($this->uses) ? (array)$this->uses : array();
734: if (!in_array($modelClass, $this->uses, true)) {
735: $this->uses[] = $modelClass;
736: }
737:
738: list($plugin, $modelClass) = pluginSplit($modelClass, true);
739:
740: $this->{$modelClass} = ClassRegistry::init(array(
741: 'class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id
742: ));
743: if (!$this->{$modelClass}) {
744: throw new MissingModelException($modelClass);
745: }
746: return true;
747: }
748:
749: /**
750: * Redirects to given $url, after turning off $this->autoRender.
751: * Script execution is halted after the redirect.
752: *
753: * @param string|array $url A string or array-based URL pointing to another location within the app,
754: * or an absolute URL
755: * @param int|array|null $status HTTP status code (eg: 301). Defaults to 302 when null is passed.
756: * @param bool $exit If true, exit() will be called after the redirect
757: * @return \Cake\Network\Response|null
758: * @triggers Controller.beforeRedirect $this, array($url, $status, $exit)
759: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::redirect
760: */
761: public function redirect($url, $status = null, $exit = true) {
762: $this->autoRender = false;
763:
764: if (is_array($status)) {
765: extract($status, EXTR_OVERWRITE);
766: }
767: $event = new CakeEvent('Controller.beforeRedirect', $this, array($url, $status, $exit));
768:
769: list($event->break, $event->breakOn, $event->collectReturn) = array(true, false, true);
770: $this->getEventManager()->dispatch($event);
771:
772: if ($event->isStopped()) {
773: return null;
774: }
775: $response = $event->result;
776: extract($this->_parseBeforeRedirect($response, $url, $status, $exit), EXTR_OVERWRITE);
777:
778: if ($url !== null) {
779: $this->response->header('Location', Router::url($url, true));
780: }
781:
782: if (is_string($status)) {
783: $codes = array_flip($this->response->httpCodes());
784: if (isset($codes[$status])) {
785: $status = $codes[$status];
786: }
787: }
788:
789: if ($status === null) {
790: $status = 302;
791: }
792: $this->response->statusCode($status);
793:
794: if ($exit) {
795: $this->response->send();
796: $this->_stop();
797: }
798:
799: return $this->response;
800: }
801:
802: /**
803: * Parse beforeRedirect Response
804: *
805: * @param mixed $response Response from beforeRedirect callback
806: * @param string|array $url The same value of beforeRedirect
807: * @param int $status The same value of beforeRedirect
808: * @param bool $exit The same value of beforeRedirect
809: * @return array Array with keys url, status and exit
810: */
811: protected function _parseBeforeRedirect($response, $url, $status, $exit) {
812: if (is_array($response) && array_key_exists(0, $response)) {
813: foreach ($response as $resp) {
814: if (is_array($resp) && isset($resp['url'])) {
815: extract($resp, EXTR_OVERWRITE);
816: } elseif ($resp !== null) {
817: $url = $resp;
818: }
819: }
820: } elseif (is_array($response)) {
821: extract($response, EXTR_OVERWRITE);
822: }
823: return compact('url', 'status', 'exit');
824: }
825:
826: /**
827: * Convenience and object wrapper method for CakeResponse::header().
828: *
829: * @param string $status The header message that is being set.
830: * @return void
831: * @deprecated 3.0.0 Will be removed in 3.0. Use CakeResponse::header().
832: */
833: public function header($status) {
834: $this->response->header($status);
835: }
836:
837: /**
838: * Saves a variable for use inside a view template.
839: *
840: * @param string|array $one A string or an array of data.
841: * @param string|array $two Value in case $one is a string (which then works as the key).
842: * Unused if $one is an associative array, otherwise serves as the values to $one's keys.
843: * @return void
844: * @link http://book.cakephp.org/2.0/en/controllers.html#interacting-with-views
845: */
846: public function set($one, $two = null) {
847: if (is_array($one)) {
848: if (is_array($two)) {
849: $data = array_combine($one, $two);
850: } else {
851: $data = $one;
852: }
853: } else {
854: $data = array($one => $two);
855: }
856: $this->viewVars = $data + $this->viewVars;
857: }
858:
859: /**
860: * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
861: *
862: * Examples:
863: *
864: * ```
865: * setAction('another_action');
866: * setAction('action_with_parameters', $parameter1);
867: * ```
868: *
869: * @param string $action The new action to be 'redirected' to.
870: * Any other parameters passed to this method will be passed as parameters to the new action.
871: * @return mixed Returns the return value of the called action
872: */
873: public function setAction($action) {
874: $this->request->params['action'] = $action;
875: $this->view = $action;
876: $args = func_get_args();
877: unset($args[0]);
878: return call_user_func_array(array(&$this, $action), $args);
879: }
880:
881: /**
882: * Returns number of errors in a submitted FORM.
883: *
884: * @return int Number of errors
885: * @deprecated 3.0.0 This method will be removed in 3.0
886: */
887: public function validate() {
888: $args = func_get_args();
889: $errors = call_user_func_array(array(&$this, 'validateErrors'), $args);
890:
891: if ($errors === false) {
892: return 0;
893: }
894: return count($errors);
895: }
896:
897: /**
898: * Validates models passed by parameters. Takes a list of models as a variable argument.
899: * Example:
900: *
901: * `$errors = $this->validateErrors($this->Article, $this->User);`
902: *
903: * @return array Validation errors, or false if none
904: * @deprecated 3.0.0 This method will be removed in 3.0
905: */
906: public function validateErrors() {
907: $objects = func_get_args();
908:
909: if (empty($objects)) {
910: return false;
911: }
912:
913: $errors = array();
914: foreach ($objects as $object) {
915: if (isset($this->{$object->alias})) {
916: $object = $this->{$object->alias};
917: }
918: $object->set($object->data);
919: $errors = array_merge($errors, $object->invalidFields());
920: }
921:
922: return $this->validationErrors = (!empty($errors) ? $errors : false);
923: }
924:
925: /**
926: * Instantiates the correct view class, hands it its data, and uses it to render the view output.
927: *
928: * @param string $view View to use for rendering
929: * @param string $layout Layout to use
930: * @return CakeResponse A response object containing the rendered view.
931: * @triggers Controller.beforeRender $this
932: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::render
933: */
934: public function render($view = null, $layout = null) {
935: $event = new CakeEvent('Controller.beforeRender', $this);
936: $this->getEventManager()->dispatch($event);
937: if ($event->isStopped()) {
938: $this->autoRender = false;
939: return $this->response;
940: }
941:
942: if (!empty($this->uses) && is_array($this->uses)) {
943: foreach ($this->uses as $model) {
944: list($plugin, $className) = pluginSplit($model);
945: $this->request->params['models'][$className] = compact('plugin', 'className');
946: }
947: }
948:
949: $this->View = $this->_getViewObject();
950:
951: $models = ClassRegistry::keys();
952: foreach ($models as $currentModel) {
953: $currentObject = ClassRegistry::getObject($currentModel);
954: if ($currentObject instanceof Model) {
955: $className = get_class($currentObject);
956: list($plugin) = pluginSplit(App::location($className));
957: $this->request->params['models'][$currentObject->alias] = compact('plugin', 'className');
958: $this->View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors;
959: }
960: }
961:
962: $this->autoRender = false;
963: $this->response->body($this->View->render($view, $layout));
964: return $this->response;
965: }
966:
967: /**
968: * Returns the referring URL for this request.
969: *
970: * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers
971: * @param bool $local If true, restrict referring URLs to local server
972: * @return string Referring URL
973: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::referer
974: */
975: public function referer($default = null, $local = false) {
976: if (!$this->request) {
977: return '/';
978: }
979:
980: $referer = $this->request->referer($local);
981: if ($referer === '/' && $default && $default !== $referer) {
982: return Router::url($default, !$local);
983: }
984: return $referer;
985: }
986:
987: /**
988: * Forces the user's browser not to cache the results of the current request.
989: *
990: * @return void
991: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::disableCache
992: * @deprecated 3.0.0 Will be removed in 3.0. Use CakeResponse::disableCache().
993: */
994: public function disableCache() {
995: $this->response->disableCache();
996: }
997:
998: /**
999: * Shows a message to the user for $pause seconds, then redirects to $url.
1000: * Uses flash.ctp as the default layout for the message.
1001: * Does not work if the current debug level is higher than 0.
1002: *
1003: * @param string $message Message to display to the user
1004: * @param string|array $url Relative string or array-based URL to redirect to after the time expires
1005: * @param int $pause Time to show the message
1006: * @param string $layout Layout you want to use, defaults to 'flash'
1007: * @return void
1008: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::flash
1009: * @deprecated 3.0.0 Will be removed in 3.0. Use Flash::set() with version 2.7+ or Session::setFlash() prior to 2.7.
1010: */
1011: public function flash($message, $url, $pause = 1, $layout = 'flash') {
1012: $this->autoRender = false;
1013: $this->set('url', Router::url($url));
1014: $this->set('message', $message);
1015: $this->set('pause', $pause);
1016: $this->set('pageTitle', $message);
1017: $this->render(false, $layout);
1018: }
1019:
1020: /**
1021: * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call.
1022: *
1023: * @param array $data POST'ed data organized by model and field
1024: * @param string|array $op A string containing an SQL comparison operator, or an array matching operators
1025: * to fields
1026: * @param string $bool SQL boolean operator: AND, OR, XOR, etc.
1027: * @param bool $exclusive If true, and $op is an array, fields not included in $op will not be
1028: * included in the returned conditions
1029: * @return array|null An array of model conditions
1030: * @deprecated 3.0.0 Will be removed in 3.0.
1031: */
1032: public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
1033: if (!is_array($data) || empty($data)) {
1034: if (!empty($this->request->data)) {
1035: $data = $this->request->data;
1036: } else {
1037: return null;
1038: }
1039: }
1040: $cond = array();
1041:
1042: if ($op === null) {
1043: $op = '';
1044: }
1045:
1046: $arrayOp = is_array($op);
1047: foreach ($data as $model => $fields) {
1048: foreach ($fields as $field => $value) {
1049: $key = $model . '.' . $field;
1050: $fieldOp = $op;
1051: if ($arrayOp) {
1052: if (array_key_exists($key, $op)) {
1053: $fieldOp = $op[$key];
1054: } elseif (array_key_exists($field, $op)) {
1055: $fieldOp = $op[$field];
1056: } else {
1057: $fieldOp = false;
1058: }
1059: }
1060: if ($exclusive && $fieldOp === false) {
1061: continue;
1062: }
1063: $fieldOp = strtoupper(trim($fieldOp));
1064: if ($fieldOp === 'LIKE') {
1065: $key = $key . ' LIKE';
1066: $value = '%' . $value . '%';
1067: } elseif ($fieldOp && $fieldOp !== '=') {
1068: $key = $key . ' ' . $fieldOp;
1069: }
1070: $cond[$key] = $value;
1071: }
1072: }
1073: if ($bool && strtoupper($bool) !== 'AND') {
1074: $cond = array($bool => $cond);
1075: }
1076: return $cond;
1077: }
1078:
1079: /**
1080: * Handles automatic pagination of model records.
1081: *
1082: * @param Model|string $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
1083: * @param string|array $scope Conditions to use while paginating
1084: * @param array $whitelist List of allowed options for paging
1085: * @return array Model query results
1086: * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::paginate
1087: */
1088: public function paginate($object = null, $scope = array(), $whitelist = array()) {
1089: return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist);
1090: }
1091:
1092: /**
1093: * Called before the controller action. You can use this method to configure and customize components
1094: * or perform logic that needs to happen before each controller action.
1095: *
1096: * @return void
1097: * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1098: */
1099: public function beforeFilter() {
1100: }
1101:
1102: /**
1103: * Called after the controller action is run, but before the view is rendered. You can use this method
1104: * to perform logic or set view variables that are required on every request.
1105: *
1106: * @return void
1107: * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1108: */
1109: public function beforeRender() {
1110: }
1111:
1112: /**
1113: * The beforeRedirect method is invoked when the controller's redirect method is called but before any
1114: * further action.
1115: *
1116: * If this method returns false the controller will not continue on to redirect the request.
1117: * The $url, $status and $exit variables have same meaning as for the controller's method. You can also
1118: * return a string which will be interpreted as the URL to redirect to or return associative array with
1119: * key 'url' and optionally 'status' and 'exit'.
1120: *
1121: * @param string|array $url A string or array-based URL pointing to another location within the app,
1122: * or an absolute URL
1123: * @param int $status Optional HTTP status code (eg: 404)
1124: * @param bool $exit If true, exit() will be called after the redirect
1125: * @return mixed
1126: * false to stop redirection event,
1127: * string controllers a new redirection URL or
1128: * array with the keys url, status and exit to be used by the redirect method.
1129: * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1130: */
1131: public function beforeRedirect($url, $status = null, $exit = true) {
1132: }
1133:
1134: /**
1135: * Called after the controller action is run and rendered.
1136: *
1137: * @return void
1138: * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1139: */
1140: public function afterFilter() {
1141: }
1142:
1143: /**
1144: * This method should be overridden in child classes.
1145: *
1146: * @param string $method name of method called example index, edit, etc.
1147: * @return bool Success
1148: * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1149: */
1150: public function beforeScaffold($method) {
1151: return true;
1152: }
1153:
1154: /**
1155: * Alias to beforeScaffold()
1156: *
1157: * @param string $method Method name.
1158: * @return bool
1159: * @see Controller::beforeScaffold()
1160: * @deprecated 3.0.0 Will be removed in 3.0.
1161: */
1162: protected function _beforeScaffold($method) {
1163: return $this->beforeScaffold($method);
1164: }
1165:
1166: /**
1167: * This method should be overridden in child classes.
1168: *
1169: * @param string $method name of method called either edit or update.
1170: * @return bool Success
1171: * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1172: */
1173: public function afterScaffoldSave($method) {
1174: return true;
1175: }
1176:
1177: /**
1178: * Alias to afterScaffoldSave()
1179: *
1180: * @param string $method Method name.
1181: * @return bool
1182: * @see Controller::afterScaffoldSave()
1183: * @deprecated 3.0.0 Will be removed in 3.0.
1184: */
1185: protected function _afterScaffoldSave($method) {
1186: return $this->afterScaffoldSave($method);
1187: }
1188:
1189: /**
1190: * This method should be overridden in child classes.
1191: *
1192: * @param string $method name of method called either edit or update.
1193: * @return bool Success
1194: * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1195: */
1196: public function afterScaffoldSaveError($method) {
1197: return true;
1198: }
1199:
1200: /**
1201: * Alias to afterScaffoldSaveError()
1202: *
1203: * @param string $method Method name.
1204: * @return bool
1205: * @see Controller::afterScaffoldSaveError()
1206: * @deprecated 3.0.0 Will be removed in 3.0.
1207: */
1208: protected function _afterScaffoldSaveError($method) {
1209: return $this->afterScaffoldSaveError($method);
1210: }
1211:
1212: /**
1213: * This method should be overridden in child classes.
1214: * If not it will render a scaffold error.
1215: * Method MUST return true in child classes
1216: *
1217: * @param string $method name of method called example index, edit, etc.
1218: * @return bool Success
1219: * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1220: */
1221: public function scaffoldError($method) {
1222: return false;
1223: }
1224:
1225: /**
1226: * Alias to scaffoldError()
1227: *
1228: * @param string $method Method name.
1229: * @return bool
1230: * @see Controller::scaffoldError()
1231: * @deprecated 3.0.0 Will be removed in 3.0.
1232: */
1233: protected function _scaffoldError($method) {
1234: return $this->scaffoldError($method);
1235: }
1236:
1237: /**
1238: * Constructs the view class instance based on the controller property
1239: *
1240: * @return View
1241: */
1242: protected function _getViewObject() {
1243: $viewClass = $this->viewClass;
1244: if ($this->viewClass !== 'View') {
1245: list($plugin, $viewClass) = pluginSplit($viewClass, true);
1246: $viewClass = $viewClass . 'View';
1247: App::uses($viewClass, $plugin . 'View');
1248: }
1249:
1250: return new $viewClass($this);
1251: }
1252:
1253: }
1254: