Stop execution after render in beforeFilter of CakePHP - cakephp

In my CakePHP 2 application i have a problem with beforeFilter.
In this thread it worked well. Because of old version of CakePHP.
In my code, If user is not authorized, I want to show him "anotherview.ctp".
I don't want to redirect visitor to another page. (because of adsense issues)
When i use "this->render" in beforeFilter, the code in my "index" action is also run.
I want to stop the execution after the last line of "beforeFilter".
When I add "exit()" to beforeFilter, it broke my code.
How can I stop execution in beforeFilter without breaking code?
class MyController extends AppController {
function beforeFilter() {
if ( $authorization == false ) {
$this->render('anotherview');
//exit();
}
}
}
function index() {
// show authorized staff
}
}

Try:
$this->response->send();
$this->_stop();

I stumbled across this thread while trying to do the same thing. The accepted answer works but omits an important detail. I'm trying to render a layout (not a view) and I had to add an additional line to prevent the original request's view from throwing errors.
Inside AppController::beforeFilter():
$this->render(FALSE, 'maintenance'); //I needed to add this
$this->response->send();
$this->_stop();

or alternatively - redirect to another view:
if ( $authorization == false ) {
$this->redirect('/users/not_authorized');
}

For CakePHP 3.5 this is what worked for me:
$event->setResult($this->render('anotherview'));
This will also enable you to use the debugger. CakePHP Debugger stopped working when I used the exit; statement.

Related

How to properly exit from a custom blackhole handler?

My problem is fairly basic: whenever an action in CakePHP is blackholed, I want to display a custom error page; the default behaviour of Cake to display a "File not found" message confuses the hell out of users (not to mention developers). So I came up with this, by searching the docs and StackOverflow:
class TestsController extends AppController
{
public $components = array ('Security');
public function beforeFilter ()
{
parent::beforeFilter ();
$this->Security->blackHoleCallback = 'blackhole';
$this->Security->csrfExpires = '+5 seconds'; // for testing
}
public function index ()
{
}
public function doit ()
{
$this->log ('Performing request; entry = ' . $this->data['foo'], 'tests');
$this->set ('foo', $this->data['foo']);
}
public function blackhole ($type)
{
$this->log ('Request has been blackholed: ' . $type, 'tests');
$this->render ('/Errors/blackhole');
$this->response->send ();
exit ();
}
}
In index.ctp there is a simple form with a single textbox, that commits to doit (excluded for brevity). This works, but I have one major issue: the exit() in the blackhole() function. The problem is, if I do not exit here doit() is still called, even if the request is blackholed, as evidenced by the log:
2013-01-30 15:37:21 Tests: Request has been blackholed: csrf
2013-01-30 15:37:21 Tests: Performing request; entry = kfkfkfkf
This is clearly not what you expect. The Cake documentation hints at using (custom) exceptions to stop processing in blackhole(), but that:
completely beats the purpose of using a custom handler;
adds another layer of complexity to something that should be simple.
My question is: is there a proper way to do an "exit" from blackhole() so that Cake does all the rendering/cleanup/etc. that it usually does; I already had to add $this->response->send() to force output to the browser. Or, althernatively, a way to tell Cake to skip calling doit() after blackhole().
My suggestion would be to redirect in your blackhole.
This is done e.g. here in the cookbook http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#usage
$this->redirect(array('controller' => 'test', 'action' => 'index'));
A redirect will issue an exit (http://book.cakephp.org/2.0/en/controllers.html#flow-control).
You can also send something nice to the user if you want, before the redirect:
$this->Session->setFlash('What are you doing!?');
In your blackhole callback you could just throw an exception with required message. That would render the proper error page and get logged too (assuming you have turned on logging for errors in core.php).

cakephp: $this->Auth->allow

When I click the Enroll Now button under admissions on my navigation bar, \merry_flowers\views\students\add.ctp should display.
Instead, the login page is getting displayed.
Does anyone know on what I'm doing wrong?
The following is my app_controller.php for the project:
class AppController extends Controller {
var $components=array('Auth','Session','Cookie');
function beforeFilter(){
if (isset($this->Auth)){
$this->Auth->userModel='MerryParent';
$this->Auth->loginAction=array('controller'=>'merry_parents','action'=>'login');
//var_dump($this->data);
$this->Auth->allow('*');
$this->Auth->loginRedirect=array('controller'=>'merry_parents','action'=>'report_card');
$this->Auth->logoutRedirect=array('controller'=>'merry_parents','action'=>'register');
$this->Auth->deny('report_card');
$this->Auth->authorize='controller';
}
else
$this->Session->setFlash('Auth has not been set');
}
function isAuthorized(){
return true;
}
Any help is much appreciated.
One gotcha is if your StudentsController has a beforeFilter, you'll need to call parent::beforeFilter or that Auth setup won't happen.
I'd also note the caveat for using authorize = 'controller'; "Remember that [isAuthorized] will be checked after you have already passed the basic authentication check against the user model.". Given that you have $this->Auth->allow('*'), auth should be skipped entirely, so I'd look for a more fundamental mistake such as the beforeFilter override.
Unfortunately nothing stands out in your pasted code. Auth problems usually involve facepalm solutions in my experience :)

Redirect to maintenance page in CakePHP doesn't work

I am trying to set a maintenance page so that when the site is disabled, it should appear no matter what page was requested.
I currently tried doing this with $this->cakeError():
in app_controller.php:
function beforeFilter(){
....
if($this->__get_config('maintenance_status') == 1){
$this->cakeError('maintenance', array('message' => $this->__get_config('maintenance_message')));
}
....
}
and in app_error.php:
function maintenance($message){
$this->controller->set('message', $message['message']);
($this->controller->RequestHandler->isAjax()) ? $this->_outputMessage('ajax_maintenance') : $this->_outputMessage('maintenance');
}
The problem is that a Fatal Error occurs, which says: Call to a member function isAjax() on a non-object. But I have obviously set the RequestHandler Component in app_controller.php. Moreover, I have tried calling this error from within another controller and it doesn't give me any Fatal Error.
What could be the problem? Why doesn't it recognize that I have initalized the Component?
From the CakePHP book:
Calling this method will show an error page to the user and halt any further processing in your application
I am assuming that you're calling the error in some callback in AppController.
If that is the case you may very likely be halting execution of your script before your components are instantiated. This would certainly cause your error.
Now, I think this error is a good chance to reevaluate how you're dealing with the problem. Is this really an error? You know the maintenance status is set so it's expected that the user be shown this page. It isn't an error. Furthermore, you certainly wouldn't want 10,000 messages in your log telling you that you turned maintenance on!
I think this could be better solved by utilizing some controller callbacks and a little bit of code.
I don't know what _get_config() is so I assume it is a custom user function that you can call in this callback.
We'll be using the beforeFilter() controller callback.
class AppController extends Controller {
public function beforeFilter() {
if ($this->_get_config('maintenance_status') === 1) {
$this->redirect('/maintenance');
}
}
}
Now, you can just setup a maintenance controller, attached to its own view, that will properly show your maintenance message, and won't log all those connection attempts during maintenance in your error log.
Slightly better would also be to use the Configure::read( "System.maintenance" ) or similar. (I tend to namespace my config data, System being the namespace for stuff like maintenance flags etc.)
Also, as Charles said - don't use an error page for an expected event. Errors are to show the user, and for the application to handle notifications etc, about unexpected failures. The maintenance page could simply be a view file in the /app/views/pages/ folder. Redirect to that if the config key is set to true/1.
Your approach seems to be intelligent, but you might be overdoing it a little.
I have a similar setup in a site I am currently developing and I simply use the auth component to take care of it for me.
To help out, I setup a new offline layout that I force the application to use if status of the site is 0 (offline). If the status is 0 app_controller denies access to the entire site.
$this->Auth->deny('*');
$this->layout = "offline";
Also, in this layout I have a hidden login form that appears if the user clicks the message.If user is able to authenticate (all users for now - development) access is granted to the entire site using the default template.
Check it out, it might help you out...
Click Here
Some of the code, but you can read more about it in the link above
function beforeFilter(){
// Site Offline = 0 , Site Online = 1
if($this->Configuration->get_site_status() == 1){
// Allow access to the site to all users and perform all required
// beforeFilter code
}else{
...
// If site is OFFLINE but User is logged in allow access.
// Later I will need to change it to only allow admin access if logged in as I am still developing
// Everyone else will be denied access even if they are able to authenticate
if(!$this->Auth->user() == null){
$this->layout = 'default';
$this->Auth->allow('*');
}else{
$this->layout = 'offline';
$this->Auth->deny('*');
}
...
}
}

Turning off debug_kit within controller action, Cakephp

I am currently working on an export function in cakephp app and im doing a query that is getting around 10,000 rows each export which cake can handle but debug_kit seems to be using lot of memory and putting me over 128mb of memory used.
I have tried tried writing this in the top of the function but debugkit is still getting involved and using large amounts of memory.
Configure::write('debug',0);
HyperCas is correct in suggesting the beforeFilter() callback as an appropriate solution.
The code could look something like this in the controller where the action (ie, export) resides:
function beforeFilter() {
// filter actions which should not output debug messages
if(in_array($this->action, array('export'))) {
Configure::write('debug', 0);
}
}
You would adjust array('export') to include all the actions you want to prevent debug.
Just to improve Benjamin Pearson's answer. Unload the component instead of turning debugging off.
public function beforeFilter() {
parent::beforeFilter();
if(in_array($this->action, array('export'))) {
$this->Components->unload('DebugKit.Toolbar');
}
}
Use
Configure::write('debug',0);
in /app/config/core.php
Or use it in the beforeFilter() callback on the controller. That would stop the debugging for the entire controller if you don't check manually for the current action (in $this->params['action']).
If your model has multiple associations you should take a look at the containable behavior
http://book.cakephp.org/view/51/Controller-Attributes
you can also switch the debug level in the config.php to 0. this will disable the debug kit automaticaly + your application will use even less memory.
Disable debug_kit on the fly
class AppController extends Controller {
public function beforeFilter() {
Configure::write('debug', 0);
}
}
in cakephp3 open bootstrap.php file in config folder
comments or remove the DebugKit loading
if (Configure::read('debug')) {
// Plugin::load('DebugKit', ['bootstrap' => true]);
}
thats all .. it will unload the DebugKit from your application

Can I make CakePHP return a suitable status code based on certain conditions?

This question is slightly related to my old post Dealing with Alias URLs in CakePHP
After much thought, I am exploring the option of having a custom 404 script in my Cake App, that is reached when a URL does not map to any controllers/actions. This script would check $this->here and look it up in a database of redirects. If a match is found it would track a specific 'promo' code and redirect.
I'm thinking status codes. Can I make my script return a suitable status code based on certain conditions? For example:
URL matches a redirect - return a 301
URL really does not have a destination - return a 404.
Can I do this?
EDIT:
What about this? Anyone see any problems with it? I put it in app_controller.
function appError($method, $params) {
//do clever stuff here
}
This should work. Assuming you redirect 404's at a LegacyUrls::map() controller action. The code needs to be stored in app/app_error.php:
<?php
class AppError extends ErrorHandler{
function error404($params) {
$Dispatcher = new Dispatcher();
$Dispatcher->dispatch('/legacy_urls/map', array('broken-url' => '/'.$params['url']));
exit;
}
function missingController($params) {
$Dispatcher = new Dispatcher();
$Dispatcher->dispatch('/legacy_urls/map', array('broken-url' => '/'.$params['url']));
exit;
}
}
?>
Good luck!
I've always created app\views\errors\missing_action.ctp and app\views\errors\missing_controller.ctp
Cake will automatically display one of those views when a url does not map out to a controller or its methods.
Unless there is a certain need for the error codes that you did not give, this would work perfectly!
I'd like to augment felixge's answer.
This version outputs a 404 error to the browser:
class AppError extends ErrorHandler
{
function _outputMessage($template)
{
if ($template === 'error404') {
$Dispatcher = new Dispatcher();
$Dispatcher->dispatch('legacy_urls/map', array('broken-url' => '/'.$params['url']));
return;
}
parent::_outputMessage($template);
}
}

Resources