CakePHP - What is the best approach to create an Admin Section - cakephp

I am looking for an insight into the best approach to create an administrator section in CakePHP. I've looked at plugins like BrowniePHP as well as others, but I am not entirely satisfied with using plugins. So I am trying to create my own which will encompass the things I need. I;ve looked at some tutorials, but cant find the right answer.
I am currently creating a vast application, which is about 10% done, but I now feel the need to have an admin section before moving on.
Basically I would like a section where I can add new articles, approve comments, deny user access, etc. This section should only be accessible by an administrator.
Also, this administrator section must be able to save to any other model.
I am still learning CakePHP and any detailed instruction would be appreciated.

to create an admin-section the first thing you have to do is to manually edit the core.php within /app/config and write the setting Routing.prefixes. This line should be around line 88 somewhere and you just have to uncomment it.
In case you can't find it, it should look like this:
Configure::write('Routing.prefixes', array('admin'));
So now you can write your admin-functions within your controllers like this:
function admin_edit($id = null) {
//your admin function
}
You don't need access to every model since your writing these function within your controllers like every other "normal" action.
You just have to connect a route to handle the admin-actions:
Router::connect('/admin/:controller/:action/*', array('admin' => true, 'prefix' => 'admin', 'controller' => 'pages'));
// 'admin' => true is a variable for you so you can check if it's an admin-action which is requested
// 'prefix' => 'admin' means that you can write function with this prefix like above
You can then access these actions via the url http://yourapp.com/admin/controller/action
If you now use the Auth-Component you can write methods for checking if a user is allowed to access these methods.
For further information please read these manual-entrys:
Prefix-Routing
Authentication (Auth-Component)

Related

Passing variable from view to controller with POST in CakePHP

I'm new to CakePHP, and am trying to do some blog-like exercise, then I ran into some problems.
See have a Model called Post, then under the PostsController I generated a view action for checking a single blog post. What I want is to allow the users to be able to add comment to the post in the Posts/view page instead of being redirected to a new Comments/add page. To do that I need to tell my CommentsController which post the user is commenting on. So wrote this in my /app/View/Posts/view.ctp:
<?php
echo $this->Form->create('Comment', array('controller' => 'comments', 'action' => 'add');
echo $this->Form->input('content', array('row' => '3'));
// this is the line I'm not sure about
echo $this->Form->input('post_id', array('default' => $post['Post']['id'], 'type' => 'hidden'));
echo $this->Form->end('Submit');
?>
Now this solution will send the value of $post['Post']['id'] to the add action in CommentsController in the form of $this->request->data['post_id'], but call me anal, I worry that whether this is the correct, or "professional" way to do this, since one can easily make the hidden field visible by altering some attributes with "inspect element' inside of any modern browser, leaving not necessarily potential security vulnerabilities, but something I personally don't feel comfortable with. So please, if anyone's worked with CakePHP before, share some experience with me on that.
First you can shorten that line to:
$this->Form->hidden('post_id', array('value' => $post['Post']['id']));
To prevent form tampering use the security component. It will:
Restricting which HTTP methods your application accepts.
CSRF protection.
Form tampering protection
Requiring that SSL be used.
Limiting cross controller communication.
Also I would validate that any data that is processed is valid. So you might want to check that the post exists and is public as well for example to prevent people can add comments to a non-public post. The same concept applies for everything: Never trust any input, not user nor API. Always validate the data and circumstances.

Filter datasource by auth.user.id

I'm a newbie to cakephp and I don't quit get it.
I'm building a system where users login and register some data. And I want the users to only see their own data. How do I do this? I was thinking about making a kind of restriction in the model or do I have to code this in every function (Connected to the views)?
I have a well functioning system With user login etc, but I can't separate the data access to the users.
I can't figure this out and I might think that's a bit because I might not know what to ask about. Hoping that some can give me a hand.
You have the logged in users id via $this->Auth->user('id');. You then have to set a condition on your find calls where user_id = $userId.
$userId = $this->Auth->user('id');
$this->SomeRelatedModel->find('all', array(
'conditions' => array(
'SomeRelatedModel.user_id' => $userId,
),
));
You can also pass the $userId variable to the model and do your find calls in there (either methods or custom find calls).
If a lot of your models/find calls need to filter by user id, you may want to create a behavior and use beforeFind callback to add the condition to the query.
When user will login, Auth will store Id in session. you can access it by using
$this->Session->read('Auth.User.id') or $this->Auth->user('id') or AuthComponent::user('id')
$this->User->find('all',array(
'conditions'=>array('id'=>$this->Session->read('Auth.User.id')),
'fields'=>'...............'));

elements or view extensions or something else? reference app?

In cakephp [2.2] I have baked everything and my "people" view is quite busy with relations and phones and addresses and other related data. I do want all of that information visible in the people view, though not quite in the baked layout.
How should I handle those portions of the related data? I'm not sure if I should use elements or extended views or plugins or what, I'm kinda new to this and the documentation wasn't clear to me (at my level) which should be used when. The baked code seemed to be a monolithic approach, so I didn't get much help looking there.
Once the user chooses to edit a phone number (for instance) from the listing on the person view, it takes them to the phone edit view and then returns them to the phone listing (index view) and not the person view that they were on. How do I get them back to the person view instead?
The blog example they provide is nice, but is there a "reference" application somewhere for cakephp that demonstrates best practices on a wide variety of their features? I couldn't find one, or anything more than just a simple app example.
Thanks, I appreciate the guidance.
This is a rather broad question, but I'm going to try and answer it. I'm not sure how advanced you're programming knowledge is, so forgive me if I'm rehashing things you already know. First, this article was a great help when I started to use the framework for the first time as it explains what code should go where and why. It's the closest I've seen to a "reference application", which would actually be a great learning tool. You could try and have a look at some of the higher profile Cake applications, like Croogo (a Cake-based CMS). But the codebase is bound to be a little bit complex.
Personally I would use elements when you want to actually reuse them in different views. The problem however, is feeding the element its data. There's a method called requestAction, but even the manual states that this should be used with moderation and in combination with caching. The problem is that using a lot of requestAction calls in different elements litters your Controllers with methods and doesn't adhere to the "Skinny Controllers, Fat Models" mantra.
I would put most of the related data calls in their respective Models and call those Model methods from the Controller and feed them to the View. So let's say you want the 10 latest PhoneNumbers and related Users.
You would have a method in your PhoneNumber model which returns an array of users and their phonenumbers. Use the Containable behaviour to limit the number of related models which are returned. The code below is an example, so the practical implementation might vary:
public function getRecentPhoneNumbers($limit=10) {
$phoneNumbers = array();
$phoneNumbers = $this->find('all', array(
'limit' => $limit,
'contain' => array('User'),
'order' => 'PhoneNumber.id DESC'
));
return $phoneNumbers;
}
If the PhoneNumber and User model are properly related you would be able to call getRecentPhoneNumbers() from the User model:
$this->PhoneNumber->getRecentPhoneNumbers(10)
Or from the Users Controller:
$this->User->PhoneNumber->getRecentPhoneNumbers(10)
Say you have an element which shows a list of those 10 numbers and it accepts a variable called $recentPhonenumbers, you then set the variable in the relevant UsersController method with the returned array from the getRecentPhoneNumbers call:
$this->set('recentPhonenumbers', $this->User->PhoneNumber->getRecentPhoneNumbers(10));
This will make it available to the View that contains the element.
The extended views are relatively new (from Cake 2.1 and onwards) and I haven't used them, but seem a great way to create conditional markup.
As for the second question, redirecting the user to the person view, rather than the index view. This is a matter of adjusting the redirect (see the manual for more details) in the edit() method of the Controller. Standard baked edit() methods accept an $id parameter you can use this to redirect to the view() (which probably also accepts an $id paramater).
So the redirect probably looks something like this:
$this->redirect(array('controller' => 'users', 'action' => 'index'));
Change it to:
$this->redirect(array('controller' => 'users', 'action' => 'view', $id));

Cake PHP: How do I make one variable avaiable across all view and elements

I want to access
id of current logged user
name of current logged user
group_id of current logged user
group_name of current logged user
across the view files, to switch menus and tabs on and off according to group_id.
How can I achieve this with minimum sacrifice of performance?
Thanks
If you use the AuthComponent, it'll store the record of the currently logged-in user in the Session under the key Auth. You can access this anywhere through the session component or helper:
$this->Session->read('Auth.User.name')
Even if you're not using the AuthComponent, the Session is the best place to store information about the current user.
Otherwise and in general, the Configure class is usually a good place to store this kind of global information:
Configure::write('User', array('id' => $id, ...));
Configure::read('User.id');
What I always do is create a AppHelper and create a method for this.
Off course this is similar to deceze's answer but it reduces some code you need to write ;)
function user($key) {
$user = $this->Session->read('Auth.User');
if (isset($user[$key])) {
return $user[$key];
}
return false;
}
Then you can call the id of the user by $this->Html->user('id');
Perhaps you could set the variables you want in your AppController (extended by all sub controllers). You should then be able to access them from all views, though be careful to name them uniquely.
CakePHP book - App Controller

cakephp acl aros_acos paradox

I'm trying to implement an authentication/authorization combo into my cakePHP site using Auth and Acl Components, but something odd is happening with my implementation. I've got the right acos, aros and aros_acos tables, and they seem to work at some level.
I have mapped my actions like this:
$this->Auth->mapActions(array('read' => array('view'), 'update' => array('edit')));
My acos table looks like this:
Site
1.1 Pages
1.2 Users
1.3 Groups
1.4 Admin
and aros table:
users
1.1 editors
1.1.1 admins
1.1.1.1 admin_name
1.2 regular_user
Users, editors and admins are groups. Admin_name is an admin user, member of the admins group, and regular_user is a member of the users group.
Now, in the aros_acos table, if I give 'users' group the CRUD rights for a 'page' like this: 0 1 1 0 (which gives them the right to read and update) then everything works fine (at least for the 'view' and 'edit' actions). But if I put 0 1 0 0 (only the right to read) then I get redirected to '/', and one particular thing that I have noticed is that it doesn't call the app_controller or at least the beforeFilter() function in the app_controller.
Moreover, I've written the beforeFilter() so that when a user does not have access to a crud, to give him a $this->flash message, letting him know that he is "not authorized" (I had to do this, as $this->Auth->authError doesn't seem to work). So, with that in mind, I now rewrite the aros_acos table for the users group like this: 0 0 1 0 ( permission only to update ) and this time I get the flash message when I access the 'view' action (which is correct since I don't have the permission to access it), but I also get the flash message when I try to access the 'edit' action.
I'm missing something, and I don't know what. I've written this question, hoping that before finishing it, I would come up with the solution myself...but no luck. I still don't know what is happening, I guess it is some controller thing...Have you got any ideas ?
Thought 1 -> Somewhere in the view page, do you have a requestAction to another page by chance? It might come from a view page or an element on a view page.
Thought 2 -> Build out your complete mapActions. This might not be an issue, but it's good to start here.
$this->Auth->mapActions(array(
'read'=>array('index','view','admin_index'),
'create'=>array('add','admin_add'),
'update'=>array('edit','admin_edit'),
'delete'=>array('delete','admin_delete')));
Don't be afraid to trace the code all the way to the Auth Component if necessary. Just pr() until you find where it's redirecting. Figure out specifically what is causing the problem.
Be sure your session is correct and doesn't get changed in the process.
Thought 3 -> Do you "rebuild" the acl tables properly? It may be a data issue. I would suggest that you use the createAco(), createAro(), and $this->Acl->allow() functions to be sure the data is correct and all the keys are correct. (never hurts to check)
This is one of those issues where you have to go step by step and trace through the app. I'm using the current stable CakePHP and haven't found any issues.

Resources