Skip to content

Commit 880ca53

Browse files
committed
Removed the Primary Investigator role and replaced it with the Create Trial and View Trial roles
Changed trial permissions to create a new permission for the owner of the trial when the trial is created Changed the trial permissions screen to prevent users that don't have the View Trial role from being added
1 parent 0901c73 commit 880ca53

File tree

11 files changed

+279
-107
lines changed

11 files changed

+279
-107
lines changed

config/common.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
'title' => 'Trials',
88
'uri' => 'OETrial',
99
'position' => 6,
10-
'restricted' => array('TaskAdministerTrials'),
10+
'restricted' => array('TaskCreateTrial', 'TaskViewTrial'),
1111
),
1212
),
1313
'module_partials' => array(

controllers/TrialController.php

+130-15
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,44 @@
55
*/
66
class TrialController extends BaseModuleController
77
{
8+
/**
9+
* The return code for actionTransitionState() if the transition was a success
10+
*/
811
const RETURN_CODE_OK = '0';
12+
/**
13+
* The return code for actionTransitionState() if the user tried to transition an open trial to in progress
14+
* while a patient is still shortlisted
15+
*/
916
const RETURN_CODE_CANT_OPEN_SHORTLISTED_TRIAL = '1';
17+
18+
/**
19+
* The return code for actionAddPermission() if the user tried to share the trial with a user that it is
20+
* already shared with
21+
*/
1022
const RETURN_CODE_USER_PERMISSION_ALREADY_EXISTS = '2';
1123

24+
25+
/**
26+
* The return code for actionRemovePermission() if all went well
27+
*/
28+
const REMOVE_PERMISSION_RESULT_SUCCESS = 'success';
29+
/**
30+
* The return code for actionRemovePermission() if the user tried to remove the last user with manage privileges
31+
*/
32+
const REMOVE_PERMISSION_RESULT_CANT_REMOVE_LAST = 'remove_last_fail';
33+
/**
34+
* The return code for actionRemovePermission() if the user tried to remove themselves from the Trial
35+
*/
36+
const REMOVE_PERMISSION_RESULT_CANT_REMOVE_SELF = 'remove_self_fail';
37+
1238
/**
1339
* @return array action filters
1440
*/
1541
public function filters()
1642
{
1743
return array(
1844
'accessControl',
19-
'ajaxOnly + getTrialList'
45+
'ajaxOnly + getTrialList',
2046
);
2147
}
2248

@@ -29,29 +55,37 @@ public function accessRules()
2955
{
3056
return array(
3157
array(
32-
'allow', // allow authenticated users to perform the 'index' action
33-
'actions' => array('index', 'getTrialList'),
58+
'allow',
59+
'actions' => array('getTrialList'),
3460
'users' => array('@'),
3561
),
62+
array(
63+
'allow',
64+
'actions' => array('index', 'userAutoComplete'),
65+
'roles' => array('TaskCreateTrial', 'TaskViewTrial'),
66+
),
3667
array(
3768
'allow',
3869
'actions' => array('view'),
70+
'roles' => array('TaskViewTrial'),
3971
'expression' => 'Trial::checkTrialAccess($user, Yii::app()->getRequest()->getQuery("id"), UserTrialPermission::PERMISSION_VIEW)',
4072
),
4173
array(
4274
'allow',
4375
'actions' => array('update', 'addPatient', 'removePatient'),
76+
'roles' => array('TaskViewTrial'),
4477
'expression' => 'Trial::checkTrialAccess($user, Yii::app()->getRequest()->getQuery("id"), UserTrialPermission::PERMISSION_EDIT)',
4578
),
4679
array(
4780
'allow',
4881
'actions' => array('permissions', 'addPermission', 'removePermission', 'transitionState'),
82+
'roles' => array('TaskViewTrial'),
4983
'expression' => 'Trial::checkTrialAccess($user, Yii::app()->getRequest()->getQuery("id"), UserTrialPermission::PERMISSION_MANAGE)',
5084
),
5185
array(
52-
'allow', // allow authenticated user to perform the 'create' action
86+
'allow',
5387
'actions' => array('create'),
54-
'users' => array('@'),
88+
'roles' => array('TaskCreateTrial'),
5589
),
5690
array(
5791
'deny', // deny all users
@@ -72,7 +106,7 @@ public function actionView($id)
72106

73107
$this->render('view', array(
74108
'model' => $model,
75-
'userPermission' => $model->getTrialAccess(Yii::app()->user->id),
109+
'userPermission' => $model->getTrialAccess(Yii::app()->user),
76110
'report' => $report,
77111
'dataProviders' => $model->getPatientDataProviders(),
78112
));
@@ -163,11 +197,9 @@ public function actionDelete($id)
163197
*/
164198
public function actionIndex()
165199
{
166-
$condition = 'trial_type = :trialType AND (
167-
owner_user_id = :userId
168-
OR EXISTS (
200+
$condition = 'trial_type = :trialType AND EXISTS (
169201
SELECT * FROM user_trial_permission utp WHERE utp.user_id = :userId AND utp.trial_id = t.id
170-
))';
202+
)';
171203

172204
$interventionTrialDataProvider = new CActiveDataProvider('Trial', array(
173205
'criteria' => array(
@@ -232,7 +264,8 @@ public function actionAddPatient($id, $patient_id, $patient_status = TrialPatien
232264
$trialPatient->patient_status = $patient_status;
233265

234266
if (!$trialPatient->save()) {
235-
throw new CHttpException(400, 'Unable to create TrialPatient: ' . print_r($trialPatient->getErrors(), true));
267+
throw new CHttpException(400,
268+
'Unable to create TrialPatient: ' . print_r($trialPatient->getErrors(), true));
236269
}
237270
}
238271

@@ -258,7 +291,8 @@ public function actionRemovePatient($id, $patient_id)
258291

259292

260293
if (!$trialPatient->delete()) {
261-
throw new CHttpException(400, 'Unable to delete TrialPatient: ' . print_r($trialPatient->getErrors(), true));
294+
throw new CHttpException(400,
295+
'Unable to delete TrialPatient: ' . print_r($trialPatient->getErrors(), true));
262296
}
263297
}
264298

@@ -328,7 +362,6 @@ public function actionAddPermission($id, $user_id, $permission)
328362
*
329363
* @param integer $id The ID of the trial
330364
* @param integer $permission_id The ID of the permission to remove
331-
* @return string 'success' if the permission is removed successfully
332365
* @throws CHttpException Thrown if the permission cannot be found
333366
* @throws CDbException Thrown if the permission cannot be deleted
334367
*/
@@ -340,9 +373,33 @@ public function actionRemovePermission($id, $permission_id)
340373
throw new CHttpException(400);
341374
}
342375

343-
$permission->delete();
376+
if ($permission->user_id === Yii::app()->user->id) {
377+
echo self::REMOVE_PERMISSION_RESULT_CANT_REMOVE_SELF;
378+
379+
return;
380+
}
381+
382+
$count = UserTrialPermission::model()->count('trial_id = :trialId AND permission = :permission',
383+
array(
384+
':trialId' => $id,
385+
':permission' => UserTrialPermission::PERMISSION_MANAGE,
386+
)
387+
);
388+
389+
if ($count <= 1) {
390+
echo self::REMOVE_PERMISSION_RESULT_CANT_REMOVE_LAST;
344391

345-
return 'success';
392+
return;
393+
}
394+
395+
396+
if (!$permission->delete()) {
397+
throw new CHttpException(500,
398+
'An error occurred when attempting to delete the permission: '
399+
. print_r($permission->getErrors(), true));
400+
}
401+
402+
echo self::REMOVE_PERMISSION_RESULT_SUCCESS;
346403
}
347404

348405
/**
@@ -357,6 +414,14 @@ protected function performAjaxValidation($model)
357414
}
358415
}
359416

417+
/**
418+
* Transitions the given Trial to a new state.
419+
* A different return code is echoed out depending on whether the transition was successful
420+
*
421+
* @param integer $id The ID of the trial to transition
422+
* @param integer $new_state The new state to transition to (must be a valid state within Trial::getAllowedStatusRange()
423+
* @throws CHttpException Thrown if an error occurs when saving
424+
*/
360425
public function actionTransitionState($id, $new_state)
361426
{
362427
/* @var Trial $model */
@@ -392,4 +457,54 @@ public function actionGetTrialList($trialID, $type)
392457

393458
echo '<div class="large-3 column trial-list">' . $dropDown . '</div>';
394459
}
460+
461+
/**
462+
* Quries users that can be assigned to the Trial and that match the search term.
463+
* Users will not be returned if they are already assigned to the trial, or if they don't have the "View Trial" permission.
464+
*
465+
* @param integer $id The trial ID
466+
* @param string $term The term to search for
467+
* @return string A JSON encoded array of users with id, label, username and value
468+
*/
469+
public function actionUserAutoComplete($id, $term)
470+
{
471+
$model = $this->loadModel($id);
472+
473+
$res = array();
474+
$term = strtolower($term);
475+
476+
$criteria = new \CDbCriteria;
477+
$criteria->compare('LOWER(username)', $term, true, 'OR');
478+
$criteria->compare('LOWER(first_name)', $term, true, 'OR');
479+
$criteria->compare('LOWER(last_name)', $term, true, 'OR');
480+
481+
$criteria->addCondition('id NOT IN (SELECT user_id FROM user_trial_permission WHERE trial_id = ' . $model->id . ')');
482+
$criteria->addCondition("EXISTS( SELECT * FROM authassignment WHERE userid = id AND itemname = 'View Trial')");
483+
484+
$words = explode(' ', $term);
485+
if (count($words) > 1) {
486+
$first_criteria = new \CDbCriteria();
487+
$first_criteria->compare('LOWER(first_name)', $words[0], true);
488+
$first_criteria->compare('LOWER(last_name)', implode(" ", array_slice($words, 1, count($words) - 1)), true);
489+
$last_criteria = new \CDbCriteria();
490+
$last_criteria->compare('LOWER(first_name)', $words[count($words) - 1], true);
491+
$last_criteria->compare('LOWER(last_name)', implode(" ", array_slice($words, 0, count($words) - 2)), true);
492+
$first_criteria->mergeWith($last_criteria, 'OR');
493+
$criteria->mergeWith($first_criteria, 'OR');
494+
}
495+
496+
$criteria->compare('active', true);
497+
498+
foreach (\User::model()->findAll($criteria) as $user) {
499+
500+
$res[] = array(
501+
'id' => $user->id,
502+
'label' => $user->getFullNameAndTitle(),
503+
'value' => $user->getFullName(),
504+
'username' => $user->username,
505+
);
506+
}
507+
508+
echo \CJSON::encode($res);
509+
}
395510
}

migrations/m170609_005053_add_primary_investigator_roles.php

-21
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
class m170704_235811_add_trial_roles extends OEMigration
4+
{
5+
const CREATE_TRIALS_ROLE = 'Create Trial';
6+
const VIEW_TRIALS_ROLE = 'View Trial';
7+
const CREATE_TRIALS_TASK = 'TaskCreateTrial';
8+
const VIEW_TRIALS_TASK = 'TaskViewTrial';
9+
10+
public function safeUp()
11+
{
12+
13+
$this->insert('authitem', array('name' => self::CREATE_TRIALS_ROLE, 'type' => 2));
14+
$this->insert('authitem', array('name' => self::CREATE_TRIALS_TASK, 'type' => 1));
15+
$this->insert('authitemchild',
16+
array('parent' => self::CREATE_TRIALS_ROLE, 'child' => self::CREATE_TRIALS_TASK));
17+
18+
$this->insert('authitem', array('name' => self::VIEW_TRIALS_ROLE, 'type' => 2));
19+
$this->insert('authitem', array('name' => self::VIEW_TRIALS_TASK, 'type' => 1));
20+
$this->insert('authitemchild',
21+
array('parent' => self::VIEW_TRIALS_ROLE, 'child' => self::VIEW_TRIALS_TASK));
22+
}
23+
24+
public function safeDown()
25+
{
26+
$this->delete('authitemchild',
27+
'parent = "' . self::CREATE_TRIALS_ROLE . '" AND child = "' . self::CREATE_TRIALS_TASK . '"');
28+
$this->delete('authitem', 'name = "' . self::CREATE_TRIALS_TASK . '"');
29+
$this->delete('authitem', 'name = "' . self::CREATE_TRIALS_ROLE . '"');
30+
31+
$this->delete('authitemchild',
32+
'parent = "' . self::VIEW_TRIALS_ROLE . '" AND child = "' . self::VIEW_TRIALS_TASK . '"');
33+
$this->delete('authitem', 'name = "' . self::VIEW_TRIALS_TASK . '"');
34+
$this->delete('authitem', 'name = "' . self::VIEW_TRIALS_ROLE . '"');
35+
}
36+
}

models/Trial.php

+39-21
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,32 @@ public static function model($className = __CLASS__)
255255
return parent::model($className);
256256
}
257257

258+
/**
259+
* Overrides CActiveModel::afterSave()
260+
*
261+
* @throws Exception Thrown if a new permission cannot be created
262+
*/
263+
protected function afterSave()
264+
{
265+
parent::afterSave();
266+
267+
if ($this->getIsNewRecord()) {
268+
269+
// Create a new permission assignment for the user that created the Trial
270+
$newPermission = new UserTrialPermission();
271+
$newPermission->user_id = Yii::app()->user->id;
272+
$newPermission->trial_id = $this->id;
273+
$newPermission->permission = UserTrialPermission::PERMISSION_MANAGE;
274+
if (!$newPermission->save()) {
275+
throw new Exception('The owner permission for the new trial could not be saved: '
276+
. print_r($newPermission->errors(), true));
277+
}
278+
}
279+
}
280+
258281
/**
259282
* Returns whether or not the given user can access the given trial using the given action
260-
* @param $user User The user to check access for
283+
* @param $user CWebUser The user to check access for
261284
* @param $trial_id int The ID of the trial
262285
* @param $permission integer The ID of the controller action
263286
* @return bool True if access is permitted, otherwise false
@@ -267,36 +290,31 @@ public static function checkTrialAccess($user, $trial_id, $permission)
267290
{
268291
/* @var Trial $model */
269292
$model = Trial::model()->findByPk($trial_id);
270-
if ($model === null) {
271-
throw new CHttpException(404);
272-
}
273-
274-
if ($model->owner_user_id === $user->id) {
275-
return true;
276-
}
277-
278-
return UserTrialPermission::model()->exists(
279-
'user_id = :userId AND trial_id = :trialId AND permission >= :permission',
280-
array(
281-
':userId' => $user->id,
282-
':trialId' => $trial_id,
283-
':permission' => $permission,
284-
)
285-
);
293+
return $model->getTrialAccess($user) >= $permission;
286294
}
287295

288-
public function getTrialAccess($user_id)
296+
/**
297+
* @param CWebUser $user The user to get access for
298+
* @return int The user permission if they have one otherwise null)
299+
*/
300+
public function getTrialAccess($user)
289301
{
290-
if ($this->owner_user_id === $user_id) {
291-
return UserTrialPermission::PERMISSION_MANAGE;
302+
if (!$user->checkAccess('TaskViewTrial')) {
303+
echo 'erhmerghesh';
304+
return null;
292305
}
293306

294307
$sql = 'SELECT MAX(permission) FROM user_trial_permission WHERE user_id = :userId AND trial_id = :trialId';
295308
$query = $this->getDbConnection()->createCommand($sql);
296309

297-
return $query->queryScalar(array(':userId' => $user_id, ':trialId' => $this->id));
310+
return $query->queryScalar(array(':userId' => $user->id, ':trialId' => $this->id));
298311
}
299312

313+
/**
314+
* Returns whether or not this trial has any shortlisted patients
315+
*
316+
* @return bool True if the trial has one or more shortlisted patients, otherwise false
317+
*/
300318
public function hasShortlistedPatients()
301319
{
302320
return TrialPatient::model()->exists('trial_id = :trialId AND patient_status = :patientStatus',

0 commit comments

Comments
 (0)