actionshrimp.com

fun and geekery

04 2009

CakePHP: PagesController with Admin Routing

Posted by Dave | Tags: , , , , ,

For small Cake websites with admin routing enabled, I like to use the Auth component to require a login for all admin routes, and allow access to everything else using the following beforeFilter in the AppController superclass:


function beforeFilter(){
    $admin = Configure::read('Routing.admin');
    if (isset($this->params[$admin]) and $this->params[$admin]){
        $this->layout = 'admin';
    }
    else {
        $this->Auth->allow();
    }
}

The problem with this however is static pages handled by the pages controller cannot be password protected. To resolve this problem, I had to overload the PagesController class that Cake comes with, and add in the required functionality. Part of the reason for doing this for me was to allow a setup where there was a password protected admin welcome page or control panel located at my_app_URL/admin, so I’ll show you the necessary routing to achieve that too.

Adding a route and overloading PagesController

First of all, copy your_app_dir/cake/libs/controller/pages_controller.php into your_app_dir/app/controllers with the rest of your application’s controllers. Then fire up an editor and take a look at your newly copied version.  You’ll see there is a function called display, this is what the pages controller uses to display pages. There is a route in app/config/routes.php that maps /pages/* to /pages/display/* to make the URL easier on the eyes, so if we’re gonna have /admin/pages/* working properly, we’ll need a similar route. Open up app/config/routes.php, and underneath the line


Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));

add in the following route:


Router::connect('/admin/pages/*', array('controller' => 'pages', 'action' => 'display', 'admin' => true));

Now, when admin routing is enabled, Cake looks for controller actions appened with “admin_”, so we’d better add in the function to handle this in PagesController. Open it up, and underneath the display function, add the following:


function admin_display() {
    $path = func_get_args();
    $temp = null;

    $count = count($path);

    if ($path[0] != 'admin') {
        //This adds admin to the beginning of the path so the pages controller will look in the 'admin' folder in pages directory
        $path = array_merge((array)'admin', $path);
    } else {
        //This removes admin from the beginning if it's there already, and sends the request round again so we end up with URLs that look like app/admin/pages/x
        //when app/admin/pages/admin/x is requested somehow.
        $path = array_slice($path, 1);
        $this->redirect(array_merge(array('controller' => 'pages', 'action' => 'display', 'admin' => true), $path));
    }

    if (!$count) {
        $this->redirect('/');
    }
    $page = $subpage = $title = null;

    if (!empty($path[0])) {
        $page = $path[0];
    }
    if (!empty($path[1])) {
        $subpage = $path[1];
    }
    if (!empty($path[$count - 1])) {
        $title = Inflector::humanize($path[$count - 1]);
    }
    $this->set(compact('page', 'subpage', 'title'));
    $this->render(join('/', $path));
}

As you can see it’s fairly similar to the display function, with a few extra lines added in that handle admin pages. There are a few subtleties here which I will explain in a second. Before that however, we also require a slight change to the existing, non-admin display function. Look for the if statement below:


if (!empty($path[0])) {
    $page = $path[0];
}

and change it to:


if (!empty($path[0])) {
    $page = $path[0];
    if ($page == 'admin') {
        //Sends admin page requests to their proper place to stop sneaky access attempts
        $this->redirect(array_merge(array('controller' => 'pages', 'action' => 'display', 'admin' => true), $path));
    }
}

Ok so what have we done?

  1. The added nested if statement in the display() function redirects requests for /pages/admin/x to their proper place, /admin/pages/x.
  2. The ‘else’ clause of the if ($path[0] == ‘admin’) in admin_display redirects requests for /admin/pages/admin/x to /admin/pages/x, which just tidies up a URL aesthetics issue.
  3. Finally, the first part of the same if statement is what handles the /admin/pages/x requests proper – it adds the ‘admin’ part back to the beginning of the $path variable that point 2 removes. This is actually just exploiting a subtlety of the pages controller, ’subpages’ (this seems to be quite hard to find in the documentation actually) – requests sent to /pages/a/b will display a page b stored in the folder your_app_dir/app/views/pages/a, rather than a page b stored in the pages root. Adding this additional logic helps organisation a bit by storing all admin pages in you_app_dir/app/views/pages/admin/.

So now this is all in place, everything should work correctly. Requests to /pages/admin/x and /admin/pages/admin/x both get sent to /admin/pages/x, and these require proper authentication.

The final step is to add in a route that allows my original use for this whole setup to work properly – displaying an admin homepage or control panel that requires authentication when a user visits your_app_URL/admin, i.e. without referring to any controllers or actions. First, create a page in your_app_dir/app/views/pages/admin/, called home.ctp that contains the content you want. You can now access this from your_app_URL/admin/pages/home, but the shorter URL works after adding the route:


Router::connect('/admin', array('controller' => 'pages', 'action' => 'display', 'admin' => true, 'home'));

Hooray! We now have an admin homepage. Hope everything worked for you, hit me with a comment if you have an issues and I’ll try and help out.


5 Responses to “CakePHP: PagesController with Admin Routing”

  1. Hey man,

    this post save my life!

    congrats!

  2. [...] is back with a tutorial on how to use the pages controller with admin routing. What I like most about their code is the lack of indention. It’s like they’re tossing [...]

  3. [...] Password protected static pages Today I came across a nice tutorial on how to protect a static page with a password Click here [...]

  4. Hi. I was trying to make it work in cakephp 1.2, but it didn’t. I did exactly the example in a test app but no succeded.
    I would like to make an admin home page with login in my app.

    Thank you.

  5. Do you get any errors or anything? And did you have the beforeFilter in the AppController class given right at the start?

Leave a Reply

Other posts:
« CakePHP Tutorial Part 2: Authentication and Tweaking |