assign roles using moodle core api functions - php

I'm creating a block in moodle to assign specific system roles to a user. Not sure the best way to do this..
I know you can assign roles from Site administration > User > Permissions > Assign System roles but my block needs to do other things in custom tables.. I have done the part which creates the record in custom tables but this system role is the only item left..and not sure how to do this
could someone please direct me to how I can do this (assign system roles using moodle core api (functions))
or if its a good idea to add record in the role_assignments table.. manually using database queries? and will it work?

$context = context_system::instance();
role_assign($roleid, $userid, $context->id);
Don't directly update the DB table, as that won't trigger event handlers for the role change (or clear relevant caches, etc.).

You can make that method in your plugin, that event will be trigger on role assign to user, for a specific course.
public static function my_plugin_role_assigned(core\event\role_assigned $enrolment_data){
global $DB;
//strange var name, better change it
$enrolment_data_data = $enrolment_data->get_data();
$snapshotid = $enrolment_data->get_data()['other']['id'];
$snapshot = $enrolment_data->get_record_snapshot('role_assignments', $snapshotid);
$roleid = $snapshot->roleid;
$rolename = $DB->get_records_sql("SELECT shortname from {role} WHERE id = ?", array($roleid));
$rolename = array_pop($rolename);
$rolename = $rolename->shortname;
if($rolename == 'editingteacher'){
//My stuff
}
}

Related

Changing database connection using Constructor for one user without effecting other users (Laravel)

I'm trying to change database connection for specific users, but the question will this affect other users?
For example, I added this Constructor to a Comment model
public function __construct(array $attributes = [])
{
if (Session::get('database') == 'new'){
$this->setConnection('newMysql');
}
parent::__construct($attributes);
}
If I have like 2000 users connected at the same time
Will this affect all queries for that comment model for one user with session = 'new' or it might affect other users as well?
Because from my understating, Constructor used for initialization and changing database connection might affect other users if they are using the comment model at the same time
Please correct me if I'm wrong.

Pass the data in the many to many relation ship in laravel

I have create a form that will show the user the event title, event description. And the code
public function show($id){
$showevents = Events::findOrFail($id);
return view('events.show',compact('showevents'));
}
This data i pass dont have the user data, but it will give me the specific event data. My question is how to pass the user data along with this?
Because my event table dont have the user information, it all in the user table.
I try {{$showevents->user->name}} in the view form, but it doesnt give me the information of the user.
You will be needing the user-id too if you want to retrieve infomation about the user and send it to the view file..
One thing you can do for getting the user-id anywhere is setting the user-id in Session variable when the user logs-in as:
\Session::set('user_id',$userID);
Then you can get the user-id anywhere in any controller using
$id = \Session::get('user_id');
For example in your case
public function show($id){
$user_id = \Session::get('user_id');
$user = User::findOrFail($user_id);
$showevents = Events::findOrFail($id);
return view('events.show',compact('showevents','user));
}
Now where you will set the Session variable depends entirely upon your code..I used to set after a user has logged in successfully and also rembember to remove session data when logging out as..
\Session::flush();
First of all {{$showevents->user->name}} of course will fail because you don't have any connection between events and user.
If you want to make connection between it you going to need user_id inside event table.
If you want to get user data but don't have any connection, you need to do in seperate query.
$user = User::findOrFail($user_id);
return view('events.show',compact('showevents', 'user'));
You should use Eloquent's relationship functions or wrap them inside you Models relationship functions and use them.
check the Laravel documentation

PHP - Would i need to create 2 separate objects to hold database data from 2 separate tables?

I may no be asking this questions right but here goes... I have a database with 2 tables "users" (for users name/password/etc) and "usersInfo" (users first name/last/address/etc). I only have 1 Users.php class- do i need 2 separate classes to create 2 objects from to hold the "users" & "usersInfo" data for the same user or will 1 class work (and still make 2 objects?)?
some of my Users.php class/
public function __construct($user = null) {
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
$this->_cookieName = Config::get('remember/cookie_name');
if(!$user) {
if(Session::exists($this->_sessionName)) {
$user = Session::get($this->_sessionName);
if($this->find($user) || $this->findUserInfo($user)) {
$this->_isLoggedIn = true;
} else {
//logout
}
}
} else {
$this->find($user);
$this->findUserInfo($user);
}
}
public function find($user = null) {
if($user) {
$field = (is_numeric($user)) ? 'id' : 'username';
$data = $this->_db->get('users', array($field, '=', $user));
if($data->count()) {
$this->_data = $data->first();
return true;
}
}
return false;
}
public function findUserInfo($user = null) {
if($user) {
$test3 = $this->_db->get('users', array('username', '=', $user));
$userId = $test3->first()->id;
$data2 = $this->_db->get('usersInfo', array('user_id', '=', $userId));
if($data2->count()) {
$this->_userInfoData = $data2->first();
return true;
}
}
return false;
}
public function data() {
return $this->_data;
}
public function userInfoData() {
return $this->_userInfoData;
}
Currently I have to create 2 objects to use all the data i need for the same user.
for example, in on of my pages.php i have:
$user = new User();
$user1 = new User($user->data()->username);
$userNane = $user->data()->username; //holds users username form "users" table
$userName1 = $user1->userInfoData()->first_name; // holds users first name from "usersInfo" table
It works but doesnt look right... is it efficient/ok practice/etc. If not, suggestions?
Also, first post, take it easy :)
What is the relationship between the 2 tables?
1-1 ? 1-*? Can a user have multiple persona? Can a persona correspond to many user accounts? Will this relation change in the future?
Depending on your answers, you might see which solution fit better your plans.
1-1 relation: you can afford to have a single class to hold related records. It will be easier to manage from the perspective of your application
Otherwise, you'll need at one time or another to handle a record separately from related records in the other table. You'll be better off with 2 distincts objects.
if you plan to change things later on for the second situation, you should keep things as they are.
In the specific case of user data, your comment bring the insight that certain data are more sensitive than others. In retrospect, I guess that's the reason you made these two tables separate. From that point of view, it is certainly better to keep both objects separate, even in a 1-1 relationship.
Regarding your code, indeed, having a dedicated UserInfo class, rather than piggy backing on another instance of User, would clearly be a good thing. A very important idea of good design is separation of concerns: you want each class to handle one and only one purpose, so that any modification to a class will have a limited scope of impact on the rest of the code.
As an example, the only thing you need to retreive a userinfo row, and therefore construct an object wrapping it, is the user id. Instead of delegating the whole job to a method of User, I would probably extract the iout of the User instance, and pass it to the adhoc UserInfo constructor or static method: there, each class only deals with things in its own perimeter. Of course, findUserInfo could also delegate to that same function.
IMO, on of the most important steps in designing/developing an app is creating a sound schema and model. Not sure how much database design experience you have, but you will want to read up on First Normal Form (1NF), and eventually (2NF and 3NF).
Part of the schema design stage is to identify all the nouns which you will reference in your app, in your case a user is a perfect example. Each of these identified nouns will then have attributes, which you will want to consider, in how each will be stored.
The problem in your situation is that you have user and user_info. As you stated user is for name, password, etc, whereas user_info is for first_name, last_name, address etc. Part of the design stage is to determine which of these attributes are directly attributable to the user object, and which are more ancillary in nature. Using your example: name, password, first_name, last_name are each directly attributable to the user noun (object), however address is more ancillary in nature, and there may be more than one address per user (billing address, vs physical address), so you may want to consider adding a user_address table. As you can see, by logically separating the attributes of the user noun (object), you start to identify relationships which make more sense (user, user_address) vs (user, user_info).
Once you identify the nouns, and separate their attributes, you can create your schema. From your schema you can use an Object Relational Mapper (ORM) like Doctrine, which will introspect your schema, and generate objects for you to use throughout your app. In your example you would end up with two objects; User and UserAddress. Also it's important that when developing your schema that you identify relationships between tables by implementing a foreign key constraints. For example, your user_address table should have a user_id column, which links to your user table. This way when doctrine introspects your schema, it will also identify these relationships, which makes coding much easier.
Once you have your ORM in place, you can then make code references like this:
// In your controller
$this->user = UserTable::findById($_SESSION['user_id']);
// Then in your view
Welcome <?php echo $user->getFirstName() ?>, to our wonderful app.
We have your addresses listed as follows:
<?php foreach ($user->getUserAddress() as $userAddress) ?>
<div>
<?php echo $address->getStreet() ?>
</div>
<?php endforeach ?>
Yes, it's a very simplistic example, but should properly demonstrate that if you design your schema properly, the code becomes semantic, which makes it easier to write, and maintain.

How to I set a field in a form to auto increment based on the primary key ID?

I'm new to Agiletoolkit and still learning the ropes.
Here's my question:
I have a regular CRUD for managing jobs.
$this->add('CRUD')->setModel('Job');
Model contains field "job_number" which is should automatically be filled-in on the "Add" page: N762, N763, etc.
Is this something that can be done in the model or the addjob page being called from the CRUD table?
Can any one give me some very simple code example?
Thanks
I believe this should be done on Model level. It basically have nothing to do with View level.
If this job_number field is strictly related with ID, then you can even make it as expression field in model and don't even store it in database. For example, job_number = id + 100.
But if you really want to store it in database, then you should probably do like this:
1) in model create job_number as ordinary field with type('number'), but with ->system(true) or ->editable(false) depending on where you want to see this field (form, grid);
2) in model init method add hook afterInsert.
function init(){
parent::init();
// ... your field definitions here
// add afterInsert hook
$this->addHook('afterInsert',array($this,'afterInsert'));
}
3) in model create method
function afterInsert($m,$new_id){ // <-- new_id is ID of newly inserted record
$m->load($new_id);
$m['job_number'] = $new_id + 100; // <-- your function here
$m->save();
}
or maybe you can even write it simpler - not sure
function afterInsert($m,$new_id){ // <-- new_id is ID of newly inserted record
$this->set('job_number',$new_id + 100); // <-- your function here
$this->save();
}
This should work, but I didn't test this. Just writing here on the fly.
Try this out and if you need more help just let us know. Also you're more than welcome to join ATK4 IRC channel and ask your questions directly.
Something like this should address your question:
in model init:
$this->addField("job_no")
->defaultValue("N" . $this->dsql()->del("fields")->field(
$this->dsql()->expr("max(id) + 1"), "nr"
)->getOne());
Not tested, though.

Best way to filter access to controller actions according to a specific client id

Using CakePHP 2.2, I am building an application in which each client has it's own "realm" of data and none of the other data is visible to them. For example, a client has his set of users, courses, contractors and jobs. Groups are shared among clients, but they cannot perform actions on groups. All clients can do with groups is assign them to users. So, an administrator (using ACL) can only manage data from the same client id.
All my objects (except groups, of course) have the client_id key.
Now, I know one way to get this done and actually having it working well, but it seems a bit dirty and I'm wondering if there is a better way. Being early in the project and new to CakePHP, I'm eager to get it right.
This is how I'm doing it now :
1- A user logs in. His client_id is written to session according to the data from the user's table.
$user = $this->User->read(null, $this->Auth->user('id'));
$this->Session->write('User.client_id', $user['User']['client_id']);
2- In AppController, I have a protected function that compares that session id to a given parameter.
protected function clientCheck($client_id) {
if ($this->Session->read('User.client_id') == $client_id) {
return true;
} else {
$this->Session->setFlash(__('Invalid object or view.'));
$this->redirect(array('controller' => 'user', 'action' => 'home'));
}
}
3- Im my different index actions (each index, each relevant controller), I check the client_id using a paginate condition.
public function index() {
$this->User->recursive = 0;
$this->paginate = array(
'conditions' => array('User.client_id' => $this->Session->read('User.client_id'))
);
$this->set('users', $this->paginate());
}
4- In other actions, I check the client_id before checking the HTTP request type this way.
$user = $this->User->read(null, $id);
$this->clientCheck($user['User']['client_id']);
$this->set('user', $user);
The concept is good - it's not 'dirty', and it's pretty much exactly the same as how I've handled situations like that.
You've just got a couple of lines of redundant code. First:
$this->Auth->user('id')
That method can actually get any field for the logged in user, so you can do:
$this->Auth->user('client_id')
So your two lines:
$user = $this->User->read(null, $this->Auth->user('id'));
$this->Session->write('User.client_id', $user['User']['client_id']);
Aren't needed. You don't need to re-read the User, or write anything to the session - just grab the client_id directly from Auth any time you need it.
In fact, if you read http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user it even says you can get it from outside the context of a controller, using the static method like:
AuthComponent::user('client_id')
Though it doesn't seem you'll be needing that.
You could also apply the client_id condition to all finds for a Model by placing something in the beforeFind function in the Model.
For example, in your User model, you could do something like this:
function beforeFind( $queryData ) {
// Automatically filter all finds by client_id of logged in user
$queryData['conditions'][$this->alias . '.client_id'] = AuthComponent::user('client_id');
return $queryData;
}
Not sure if AuthComponent::user('client_id') works in the Model, but you get the idea. This will automatically apply this condition to every find in the model.
You could also use the beforeSave in the model to automatically set that client_id for you in new records.
My answer may be database engine specific as I use PostgreSQL. In my project I used different schema for every client in mysql terms that would be separate database for every client.
In public schema (common database) I store all data that needs to be shared between all clients (objects that do not have client_id in your case), for example, variable constants, profile settings and so on.
In company specific models I define
public $useDbConfig = 'company_data';
In Controller/AppController.php beforeFilter() method I have this code to set schema according to the logged in user.
if ($this->Session->check('User.Company.id')) {
App::uses('ConnectionManager', 'Model');
$dataSource = ConnectionManager::getDataSource('company_data');
$dataSource->config['schema'] =
'company_'.$this->Session->read('User.Company.id');
}
As you see I update dataSource on the fly according to used company. This does exclude any involvement of company_id in any query as only company relevant data is stored in that schema (database). Also this adds ability to scale the project.
Downside of this approach is that it creates pain in the ass to synchronize all database structures on structure change, but it can be done using exporting data, dropping all databases, recreating them with new layout and importing data back again. Just need to be sure to export data with full inserts including column names.

Categories