I am working with CakePhp 2.x. I have three Columns:
User | Course | UserCourseRole
Each user can edit multiple courses and one course can be edited by multiple users. So far so good.
If a user wants to see an index of all the courses i want to show a 'edit'-link only next to the courses which he can in fact edit.
How can i realize this? I figured i would have to set some sort of extra field inside the CourseController and check for this field inside the view. Is this the right way to go?
My current Code is
CourseController.php
...
public function index() {
$courses = $this->Course->find('all', array('recursive' => 2));
$this->set('courses', $courses);
}
...
Courses/index.ctp
<!-- File: /app/View/Courses/index.ctp -->
...
<?php foreach ($courses as $course):?>
...
<?php
echo $this->Html->link('edit', array('action' => 'edit', $course['Course']['id']));
?>
...
In beforeRender() or beforeFilter() set $this->Auth->user() as a variable to the view, for example as userData.
$this->set('userData', $this->Auth->user());
Implement a (auth)helper that uses that variable (you can make it configurable as a helper setting) and do your checks like:
if ($this->Auth->hasRole($course['Course']['role']) { /* ... */ }
if ($this->Auth->isLoggedIn() { /* ... */ }
if ($this->Auth->isMe($course['Course']['user_id']) { /* ... */ }
Implement the hasRole() method according to whatever your specific requirements are.
Doing this as helper as a bunch of advantages, it is easy to reuse, overload and adapt to whatever your checks are and you don't use a component in a view plus that you should avoid calling statics and singletons a lot in your app. Also it is pretty easy to read and understand what the code does.
I think the good idea is set some variable or constans after logged (if user has privileges) and uses if statement for check.
if($allow === true) {
echo $html->link('Edit',...
}
or use AuthComponent::user() in Views.
This idea it's not good if we can many kind of admins (admin, moderator, reviewier, etc.)
Maybe someone will have a better solution
Related
In our Cakephp3 application, the user is inputting some text with apostrophe's and it should be backslashed or using mysql_real_escape_string() we should be handled to override the errors throwing in site.
This fix should be done in one uniq place, instead of being taken care in all the places.
What would be the best approach?
Thanks
Maybe I reinventing the wheel, but cake provides methods to correctly save and display any data which user tries to "inject".
In trivial case, if the user wanna save his nickname as 105; DROP TABLE users or <script>location.href="pornhub"</script> - You should allow him to use that nickname, and if You use standard model - there's no way to inject anything. When You try to display users data back in the layout, just use h($user->nickname)
I recommed you to put a str_replace at your tables before marshall.
If this is needed for all tables, I recommend you to put the before marshall at Table.php and extend it in yours others tables
It should be something like this:
At table.php:
public function beforeMarshal(Event $event, ArrayObject $data,
ArrayObject $options)
{
foreach ($data as $key => $value) {
if (is_string($value)) {
$data[$key] = str_replace("'","`",$value);
}
}
}
At the other tables:
class YourTableNameTable extends Table
Read the following: https://book.cakephp.org/3.0/en/orm/saving-data.html#modifying-request-data-before-building-entities
I'm working with cakephp3. I want to make login page. Name of table in Accounting database is 'users'.
This is my code:
<?php
namespace App\Controller;
use App\Controller\AppController;
class UsersController extends AppController {
public function login() {
if ($this->request->is('post')) {
$data = $this->request->data;
$cnt = $data->Users->find()
->count();
if ($cnt > 0) {
$this->redirect(['action' => 'index']);
} else {
$this->set('error', 'username or password is incorrct ');
}
}
}}
and this is Users.php
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class UsersTable extends Table {
}
after login in login page:
Error: Call to a member function find() on a non-object
In your opinion, what is the problem.
$data is not a Table object.
$data = $this->request->data;
$cnt = $data->Users->find()
This is pretty obvious.
I strongly recommend you to take some time and learn about debugging techniques and how to tackle this kind of problem and error messages. A developer should be able to resolve this kind of problem pretty quickly without external help. This is considered normal ever days work for a developer.
1) Read the whole error message 2) Search for it on Google and Stackoverflow, it is very unlikely nobody else ever got that message before. 3) Act according to whatever the cause of the error message is.
In the case of this error message debug what kind of object you're dealing with and figure out why it is not the object you expect it to be. Going trough the call stack helps. Use Xdebugs profiler for that, it's a great tool.
Also don't use variable names like $cnt I assume this is supposed to mean "account" which doesn't even fit into the context it is used. It's very bad named. Instead use proper variable names that are readable and fit into the context. It is a totally wrong assumption that keeping variable names short is any kind of time saver - it is clearly not. The next person working with this will need a dictionary or do a lot of guesswork on what these variables mean.
Instead of $cnt = $data->Users->find()->count(); use $cnt = $this->{$this->modelClass}->find('count');
I have an application in which we give a very friendly interface for managing data. This is done through many controllers' add/edit/view functions. But now the requirement has come that we should have "super admins" able to edit anything, and scaffolding will give them a quick and dirty manner of changing data. Since scaffolding uses add/edit/view by default, I've unintentionally overwritten the ability to scaffold.
I can't just go and change all my calls to edit/add for our "user friendly" data managing. So I want to essentially ignore the add/edit/view when, for example, a user has a flag of "yes, please let me scaffold". I imagined it would be something like:
public function edit($id) {
if (admin_user) {
$scaffold;
} else {
[user-friendly version code]
}
}
But no dice. How can I achieve what I want?
suppose you already have admin users and you want to scaffold only super-user:
Also suppose you store the information about beeing a super-user or not in a column named super in the users table
in your core.php
Configure::write('Routing.prefixes', array('admin', 'super));
in your appController
public $scaffold = 'super';
beforFilter() {
if($this->Auth->user('super') && !isset($this->params['super'])
$this->redirect(array('super' => true));
}
Now I can't try this code but the idea should work.
edit: we need to check if we are already in a super_action to avoid infinite redirect
I thought this would be a relatively common thing to do, but I can't find examples anywhere, and the Cookbook's section on find() was not clear in the slightest on the subject. Maybe it's just something that's so simple Cake assumes you can just do it on your own.
All I'm looking to do here is retrieve a User's name (not the currently logged-in user…a different one) in Cake based on their ID passed to my by an array in the view.
Here's what I've got in the controller:
public function user_lookup($userID){
$this->User->flatten = false;
$this->User->recursive = 1;
$user = $this->User->find('first', array('conditions' => $userID));
//what now?
}
At this point, I don't even know if I'm on the right track…I assume this will return an array with the User's data, but how do I handle those results? How do I know what the array's gonna look like? Do I just return($cakeArray['first'].' '.$cakeArray['last'])? I dunno…
Help?
You need to use set to take the returned data, and make it accessible as a variable in your views. set is the main way you send data from your controller to your view.
public function user_lookup($userID){
$this->User->flatten = false;
$this->User->recursive = 1;
// added - minor improvement
if(!$this->User->exists($userID)) {
$this->redirect(array('action'=>'some_place'));
// the requested user doesn't exist; redirect or throw a 404 etc.
}
// we use $this->set() to store the data returned.
// It will be accessible in your view in a variable called `user`
// (or what ever you pass as the first parameter)
$this->set('user', $this->User->find('first', array('conditions' => $userID)));
}
// user_lookup.ctp - output the `user`
<?php echo $user['User']['username']; // eg ?>
<?php debug($user); // see what's acutally been returned ?>
more in the manual (this is fundamental cake stuff so might be worth having a good read)
I have a form in CakePHP with a few dozen fields in it. From all the examples I have seen, there is duplicate form code for an add view and an edit view.
Is there any tricks to keep the duplication out? What is best method in CakePHP for this?
What I do, is to put all form fields in an element, and then insert the element in the add.ctp and edit.ctp
Don't forget to add the hidden field with the id in the edit.ctp
This way all visible elements are in one file, easier to maintain.
View/MyModel/add.ctp
echo $this->Form->create('MyModel');
echo $this->element('my_form');
echo $this->Form->end();
View/MyModel/edit.ctp
echo $this->Form->create('MyModel');
echo $this->Form->input('id');
echo $this->element('my_form');
echo $this->Form->end();
View/Elements/my_form.ctp
// your form inputs
// whatever they are
You should NOT merge those views, because add/edit are different actions and deserve separate view files. As your application grows you will realize that its good to have separate views to reduce complexity of if else conditions.
If you still want to avoid the separate files, Use
function add() {
.....
$this->render('edit')
}
I've done this before, but reverted back to having separate views, mainly for my own sanity.
It's easy enough to do. The edit requires an input for the record id. This is usually hidden. Any default form values for the add form will have to be contained in conditionals so that the stored values are not overwritten with defaults when you are editing a record
On the controller side of things, you'll need a conditional statement to decide whether to act as an add or edit depending on whether the $this->data['MyModel']['id'] is set.
I think that covers it - if I think of anything else I'll add it in.
My work pattern tends to be to build the edit view, then copy and paste to create the basis for the add view.
this code will check if you have admin_form.ctp or form.ctp which will make it use the same code for add / edit
https://github.com/infinitas/infinitas/blob/dev/app_controller.php#L389
1.3 submits the forms automatically to where the are from so when you go to /edit/1 it will post to there, and /add will post to add.
that is all you need to do. if you have a edit that is vastly different to the add, then you just create the 2 files. when you want them the same, just make the one.
in your app controller
public function render($view = null, $layout = null) {
$viewPaths = App::path('View', $this->plugin);
$rootPath = $viewPaths[0] . DS . $this->viewPath . DS;
$requested = $rootPath . $view . '.ctp';
if (in_array($this->request->action, array('admin_edit', 'admin_add', 'edit', 'add'))) {
$viewPath = $rootPath . $this->request->action . '.ctp';
if (!file_exists($requested) && !file_exists($viewPath)) {
if (strpos($this->request->action, 'admin_') === false) {
$view = 'form';
} else {
$view = 'admin_form';
}
}
}
return parent::render($view, $layout);
}
and in your view you can always check whether its edit or add
if ($this->request->params['action'] == 'admin_edit') {
//do something
}
if ($this->request->params['action'] == 'admin_add') {
//do something
}
in edit.ctp
if($this->data[ModelName]['id']) {
$this->Form->input('id');
}
// create rest of the fields
in Controller::add()
$this->autoRender=false; // at the start of function
$this->render('edit.ctp'); // at the point where you actually want to render
no need to create add.ctp