diff --git a/SQL/Archive/17.1/2017-03-30_Project_level_permissions.sql b/SQL/Archive/17.1/2017-03-30_Project_level_permissions.sql new file mode 100644 index 00000000000..bfd77e81aa8 --- /dev/null +++ b/SQL/Archive/17.1/2017-03-30_Project_level_permissions.sql @@ -0,0 +1,12 @@ +CREATE TABLE `user_project_rel` ( + `UserID` int(10) unsigned NOT NULL, + `ProjectID` int(2) NOT NULL, + PRIMARY KEY (`UserID`,`ProjectID`), + KEY `FK_user_project_rel_2` (`ProjectID`), + CONSTRAINT `FK_user_project_rel_2` FOREIGN KEY (`ProjectID`) REFERENCES `Project` (`ProjectID`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_user_project_rel_1` FOREIGN KEY (`UserID`) REFERENCES `users` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT INTO user_project_rel (UserID, ProjectID) SELECT 1, ProjectID FROM Project; +INSERT INTO ConfigSettings (Name, Description, Visible, AllowMultiple, DataType, Parent, Label, OrderNumber) SELECT 'useProjectPermissions', "Enable project level permissions", 1, 0, 'boolean', ID, 'Use project-level permissions', 4 FROM ConfigSettings WHERE Name="study"; +INSERT INTO Config (ConfigID, Value) SELECT ID, "false" FROM ConfigSettings WHERE Name="useProjectPermissions"; diff --git a/modules/candidate_list/php/NDB_Menu_Filter_candidate_list.class.inc b/modules/candidate_list/php/NDB_Menu_Filter_candidate_list.class.inc index 35e91f9a459..93bec36b718 100644 --- a/modules/candidate_list/php/NDB_Menu_Filter_candidate_list.class.inc +++ b/modules/candidate_list/php/NDB_Menu_Filter_candidate_list.class.inc @@ -100,6 +100,7 @@ class NDB_Menu_Filter_Candidate_List extends NDB_Menu_Filter $this->validFilters = array( 'pso.ID', 'c.CenterID', + 'c.ProjectID', 'c.CandID', 'c.PSCID', 'c.Gender', @@ -141,6 +142,11 @@ class NDB_Menu_Filter_Candidate_List extends NDB_Menu_Filter $this->query .= " AND c.CenterID IN (" . $site_arr . ")"; } + if ($config->getSetting("useProjectPermissions")) { + $projectIds = $user->getProjectIds(); + $this->query .= " AND c.ProjectID IN (" . implode(",", $projectIds) . ")"; + } + //'COALESCE(pso.ID,1) AS Participant_Status', $this->group_by = 'c.CandID, psc.Name, c.PSCID, c.Gender'; $this->order_by = 'c.PSCID ASC'; @@ -320,7 +326,11 @@ class NDB_Menu_Filter_Candidate_List extends NDB_Menu_Filter // Project list, if applicable if ($config->getSetting("useProjects")==="true") { $list_of_projects = array(null => 'All'); - $projectList = Utility::getProjectList(); + if ($config->getSetting("useProjectPermissions")) { + $projectList = $user->getProjects(); + } else { + $projectList = Utility::getProjectList(); + } foreach ($projectList as $key => $value) { $list_of_projects[$key] =$value; } diff --git a/modules/candidate_parameters/php/NDB_Form_candidate_parameters.class.inc b/modules/candidate_parameters/php/NDB_Form_candidate_parameters.class.inc index 6baf4d1b7d2..2e2c659f699 100644 --- a/modules/candidate_parameters/php/NDB_Form_candidate_parameters.class.inc +++ b/modules/candidate_parameters/php/NDB_Form_candidate_parameters.class.inc @@ -41,6 +41,14 @@ class NDB_Form_Candidate_Parameters extends NDB_Form { //create user object $user =& User::singleton(); + $config =& NDB_Config::singleton(); + $candidate =& Candidate::singleton($this->identifier); + + if ($config->getSetting("useProjectPermissions")) { + if (!$user->hasAccessToProject($candidate->getData("ProjectID"))) { + return false; + } + } // Set global permission to control access // to different modules of candidate_parameters page diff --git a/modules/create_timepoint/php/create_timepoint.class.inc b/modules/create_timepoint/php/create_timepoint.class.inc index 94041a45d51..040d196a071 100644 --- a/modules/create_timepoint/php/create_timepoint.class.inc +++ b/modules/create_timepoint/php/create_timepoint.class.inc @@ -39,9 +39,15 @@ class Create_Timepoint extends \NDB_Form { // create user object $user =& \User::singleton(); - + $config =& \NDB_Config::singleton(); $candidate =& \Candidate::singleton($this->identifier); + if ($config->getSetting("useProjectPermissions")) { + if (!$user->hasAccessToProject($candidate->getData("ProjectID"))) { + return false; + } + } + // check user permissions return ( $user->hasPermission('data_entry') && diff --git a/modules/dashboard/php/NDB_Form_dashboard.class.inc b/modules/dashboard/php/NDB_Form_dashboard.class.inc index be9cea3b31c..2741a72523d 100644 --- a/modules/dashboard/php/NDB_Form_dashboard.class.inc +++ b/modules/dashboard/php/NDB_Form_dashboard.class.inc @@ -73,25 +73,34 @@ class NDB_Form_Dashboard extends NDB_Form } } + $useProjects = $config->getSetting('useProjects'); + $useProjectPermissions = $config->getSetting('useProjectPermissions'); + $projectIDs = $useProjectPermissions ? + $user->getProjectIds() : + array_keys(Utility::getProjectList()); + $recruitmentTarget = $config->getSetting('recruitmentTarget'); + + $overallRecruitment = Project::getRecruitment($projectIDs); + $this->createProjectProgressBar( 'overall', "Overall Recruitment", $recruitmentTarget, - $this->getTotalRecruitment() + $overallRecruitment, + $projectIDs ); - $useProjects = $config->getSetting('useProjects'); $this->tpl_data['useProjects'] = $useProjects; if ($useProjects == "true") { - $projects = Utility::getProjectList(); - foreach ($projects as $projectID => $project) { + foreach ($projectIDs as $projectID) { $projectInfo = $config->getProjectSettings($projectID); $this->createProjectProgressBar( $projectID, $projectInfo['Name'], $projectInfo['recruitmentTarget'], - $this->getTotalRecruitmentByProject($projectID) + Project::getRecruitment(array($projectID)), + $projectIDs ); } } @@ -102,8 +111,9 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN session s ON (s.ID=f.SessionID) LEFT JOIN candidate c ON (s.CandID=c.CandID) WHERE s.Active='Y' AND c.Active='Y' + AND c.ProjectID IN (:projectIDs) AND s.CenterID <> 1", - array() + array('projectIDs' => implode(',', $projectIDs)) ); // Tasks @@ -118,8 +128,9 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN candidate c ON (s.CandID=c.CandID) WHERE fqc.QCStatus IS NULL AND s.Active='Y' AND c.Active='Y' + AND c.ProjectID IN (:projectIDs) AND s.CenterID <> 1", - array() + array('projectIDs' => implode(',', $projectIDs)) ); $this->tpl_data['new_scans_site'] = 'Sites: all'; } @@ -133,8 +144,9 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN session s ON (flag.SessionID=s.ID) LEFT JOIN candidate c ON (s.CandID=c.CandID) WHERE s.CenterID <> 1 + AND c.ProjectID IN (:projectIDs) AND s.Active='Y' AND c.Active='Y'", - array() + array('projectIDs' => implode(',', $projectIDs)) ); $this->tpl_data['conflicts_site'] = 'Sites: all'; } else { @@ -145,8 +157,12 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN candidate c ON (c.CandID=s.CandID) LEFT JOIN psc ON (psc.CenterID=s.CenterID) WHERE FIND_IN_SET(psc.CenterID, :siteID) + AND c.ProjectID IN (:projectIDs) AND s.Active='Y' AND c.Active='Y'", - array('siteID' => implode(',', $siteID)) + array( + 'siteID' => implode(',', $siteID), + 'projectIDs' => implode(',', $projectIDs) + ) ); $this->tpl_data['conflicts_site'] = 'Sites: All User Sites'; } @@ -159,8 +175,9 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN session s ON (s.ID=flag.SessionID) LEFT JOIN candidate c ON (s.CandID=c.CandID) WHERE flag.Data_entry='In Progress' + AND c.ProjectID IN (:projectIDs) AND s.Active='Y' AND c.Active='Y' AND s.CenterID <> 1", - array() + array('projectIDs' => implode(',', $projectIDs)) ); $this->tpl_data['incomplete_forms_site'] = 'Sites: all'; } else { @@ -171,8 +188,12 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN psc ON (psc.CenterID=s.CenterID) WHERE Data_entry='In Progress' AND FIND_IN_SET(psc.CenterID, :siteID) + AND c.ProjectID IN (:projectIDs) AND s.Active='Y' AND c.Active='Y'", - array('siteID' => implode(',', $siteID)) + array( + 'siteID' => implode(',', $siteID), + 'projectIDs' => implode(',', $projectIDs) + ) ); $this->tpl_data['incomplete_forms_site'] = $site; } @@ -187,8 +208,9 @@ class NDB_Form_Dashboard extends NDB_Form LEFT JOIN session s ON (s.ID=fg.SessionID) LEFT JOIN candidate c ON (c.CandID=s.CandID) WHERE Review_Done IS NULL + AND c.ProjectID IN (:projectIDs) AND c.Active='Y' AND s.Active='Y'", - array() + array('projectIDs' => implode(',', $projectIDs)) ); $this->tpl_data['radiology_review_site'] = 'Sites: all'; } @@ -222,10 +244,11 @@ class NDB_Form_Dashboard extends NDB_Form $this->tpl_data['violated_scans'] = $DB->pselectOne( "SELECT COUNT(*) FROM mri_protocol_violated_scans LEFT JOIN candidate c USING (CandID) - WHERE COALESCE(c.CenterID, 0) <> 1", + WHERE COALESCE(c.CenterID, 0) <> 1 + AND c.ProjectID IN (:projectIDs)", /* include null CenterIDs so we don't accidentally filter things out */ - array() + array('projectIDs' => implode(',', $projectIDs)) ); $this->tpl_data['violated_scans_site'] = 'Sites: all'; } @@ -308,87 +331,6 @@ class NDB_Form_Dashboard extends NDB_Form } } - /** - * Gets the total count of candidates associated with a specific project - * - * @return int - */ - function getTotalRecruitment() - { - $DB = Database::singleton(); - $totalRecruitment = $DB->pselectOne( - "SELECT COUNT(*) FROM candidate c - WHERE c.Active='Y' AND c.Entity_type='Human' AND c.CenterID <> 1", - array() - ); - return $totalRecruitment; - } - - /** - * Gets the total count of candidates associated with a specific project - * - * @param int $projectID Project ID - * - * @return int - */ - function getTotalRecruitmentByProject($projectID) - { - $DB = Database::singleton(); - $totalRecruitment = $DB->pselectOne( - "SELECT COUNT(*) - FROM candidate c - WHERE c.Active='Y' AND c.ProjectID=:PID AND c.Entity_type='Human' - AND c.CenterID <> 1", - array('PID' => $projectID) - ); - return $totalRecruitment; - } - - /** - * Gets the total count of candidates of a specific gender - * - * @param string $gender gender (male or female) - * - * @return int - */ - function getTotalGender($gender) - { - $DB = Database::singleton(); - $total = $DB->pselectOne( - "SELECT COUNT(c.CandID) - FROM candidate c - WHERE c.Gender=:Gender AND c.Active='Y' AND c.Entity_type='Human' - AND c.CenterID <> 1", - array('Gender' => $gender) - ); - return $total; - } - - /** - * Gets the total count of candidates of a specific gender, - * associated with a specific project - * - * @param string $gender gender (male or female) - * @param int $projectID Project ID - * - * @return int - */ - function getTotalGenderByProject($gender, $projectID) - { - $DB = Database::singleton(); - $total = $DB->pselectOne( - "SELECT COUNT(c.CandID) - FROM candidate c - WHERE c.Gender=:Gender AND c.Active='Y' AND c.ProjectID=:PID - AND c.Entity_type='Human' AND c.CenterID <> 1", - array( - 'Gender' => $gender, - 'PID' => $projectID, - ) - ); - return $total; - } - /** * Creates the template data for a progress bar * @@ -411,18 +353,18 @@ class NDB_Form_Dashboard extends NDB_Form = $recruitmentTarget; if ($ID == 'overall') { - $totalFemales = $this->getTotalGender("Female"); + $totalFemales = Project::getTotalFemales(); } else { - $totalFemales = $this->getTotalGenderByProject("Female", $ID); + $totalFemales = Project::getTotalFemales(array($ID)); } $this->tpl_data['recruitment'][$ID]['female_total'] = $totalFemales; $this->tpl_data['recruitment'][$ID]['female_percent'] = round($totalFemales / $recruitmentTarget * 100); if ($ID == 'overall') { - $totalMales = $this->getTotalGender("Male"); + $totalMales = Project::getTotalMales(); } else { - $totalMales = $this->getTotalGenderByProject("Male", $ID); + $totalMales = Project::getTotalMales(array($ID)); } $this->tpl_data['recruitment'][$ID]['male_total'] = $totalMales; $this->tpl_data['recruitment'][$ID]['male_percent'] diff --git a/modules/imaging_browser/php/NDB_Menu_Filter_imaging_browser.class.inc b/modules/imaging_browser/php/NDB_Menu_Filter_imaging_browser.class.inc index 20fc3daa7bb..4832dac9bfc 100644 --- a/modules/imaging_browser/php/NDB_Menu_Filter_imaging_browser.class.inc +++ b/modules/imaging_browser/php/NDB_Menu_Filter_imaging_browser.class.inc @@ -122,6 +122,11 @@ class NDB_Menu_Filter_Imaging_Browser extends NDB_Menu_Filter $config =& NDB_Config::singleton(); $user =& User::singleton(); $DB = Database::singleton(); + + if ($config->getSetting("useProjectPermissions")) { + $this->query .= " AND c.ProjectID IN (" . implode(",", $user->getProjectIds()) . ")"; + } + if (!$user->hasPermission('imaging_browser_view_allsites')) { $site_arr = implode(",", $user->getCenterIDs()); $this->query .= " AND c.CenterID IN (" . $site_arr . ")"; @@ -269,7 +274,12 @@ class NDB_Menu_Filter_Imaging_Browser extends NDB_Menu_Filter $config =& NDB_Config::singleton(); if ($config->getSetting('useProjects') === "true") { $list_of_projects = $allAr; - $projectList = Utility::getProjectList(); + if ($config->getSetting("useProjectPermissions")) { + $projectList = $user->getProjects(); + } else { + $projectList = Utility::getProjectList(); + } + foreach ($projectList as $key => $value) { $list_of_projects[$key] =$value; } diff --git a/modules/instrument_list/php/NDB_Menu_Filter_instrument_list.class.inc b/modules/instrument_list/php/NDB_Menu_Filter_instrument_list.class.inc index fa35bc1b949..1060b4d46ed 100644 --- a/modules/instrument_list/php/NDB_Menu_Filter_instrument_list.class.inc +++ b/modules/instrument_list/php/NDB_Menu_Filter_instrument_list.class.inc @@ -38,12 +38,19 @@ class NDB_Menu_Filter_Instrument_List extends NDB_Menu_Filter { // create user object $user =& User::singleton(); + $config =& NDB_Config::singleton(); $timePoint =& TimePoint::singleton($_REQUEST['sessionID']); $candID = $timePoint->getCandID(); $candidate =& Candidate::singleton($candID); + if ($config->getSetting("useProjectPermissions")) { + if (!$user->hasAccessToProject($candidate->getData("ProjectID"))) { + return false; + } + } + // check user permissions return ($user->hasPermission('access_all_profiles') || (in_array( diff --git a/modules/new_profile/php/NDB_Form_new_profile.class.inc b/modules/new_profile/php/NDB_Form_new_profile.class.inc index cb84565d08f..5ea6d34bb69 100644 --- a/modules/new_profile/php/NDB_Form_new_profile.class.inc +++ b/modules/new_profile/php/NDB_Form_new_profile.class.inc @@ -115,6 +115,7 @@ class NDB_Form_New_Profile extends NDB_Form function new_profile()//@codingStandardsIgnoreLine { $config =& NDB_Config::singleton(); + $user =& User::singleton(); $startYear = $config->getSetting('startYear'); $endYear = $config->getSetting('endYear'); $ageMax = $config->getSetting('ageMax'); @@ -185,7 +186,11 @@ class NDB_Form_New_Profile extends NDB_Form } if ($config->getSetting("useProjects") == "true") { - $projects = Utility::getProjectList(); + if ($config->getSetting("useProjectPermissions")) { + $projects = $user->getProjects(); + } else { + $projects = Utility::getProjectList(); + } $projList = array('' => ''); foreach ($projects as $projectID => $projectName) { $projList[$projectID] = $projectName; @@ -298,6 +303,15 @@ class NDB_Form_New_Profile extends NDB_Form $errors['ProjectID'] = "Project is required"; } + $useProjectsPermissions = $config->getSetting('useProjectPermissions'); + if ($useProjectsPermissions === "true" && !empty($values['ProjectID'])) { + $user =& User::singleton(); + + if (!$user->hasAccessToProject($values['ProjectID'])) { + $errors['ProjectID'] = "User does not belong to this project"; + } + } + return $errors; } diff --git a/modules/timepoint_list/php/NDB_Menu_timepoint_list.class.inc b/modules/timepoint_list/php/NDB_Menu_timepoint_list.class.inc index ffef8edf499..a986af5bd40 100644 --- a/modules/timepoint_list/php/NDB_Menu_timepoint_list.class.inc +++ b/modules/timepoint_list/php/NDB_Menu_timepoint_list.class.inc @@ -33,9 +33,15 @@ class NDB_Menu_Timepoint_List extends NDB_Menu { // create user object $user =& User::singleton(); - + $config =& NDB_Config::singleton(); $candidate =& Candidate::singleton($_REQUEST['candID']); + if ($config->getSetting("useProjectPermissions")) { + if (!$user->hasAccessToProject($candidate->getData("ProjectID"))) { + return false; + } + } + // check user permissions if ($user->hasPermission('access_all_profiles') || (in_array( diff --git a/modules/user_accounts/php/NDB_Form_user_accounts.class.inc b/modules/user_accounts/php/NDB_Form_user_accounts.class.inc index 20174d28d4f..10a133a6b4a 100644 --- a/modules/user_accounts/php/NDB_Form_user_accounts.class.inc +++ b/modules/user_accounts/php/NDB_Form_user_accounts.class.inc @@ -242,6 +242,24 @@ class NDB_Form_User_Accounts extends NDB_Form unset($values['CenterIDs']); // END multi-site UPDATE } + + // multi-project UPDATE + $us_curr_projects = $values['ProjectIDs']; + if (!$this->isCreatingNewUser()) { + $DB->delete('user_project_rel', array("UserID" => $uid)); + foreach ($us_curr_projects as $project) { + $DB->insert( + 'user_project_rel', + array( + "UserID" => $uid, + "ProjectID" => $project, + ) + ); + } + } + unset($values['ProjectIDs']); + // END multi-project UPDATE + // EXAMINER UPDATE $ex_curr_sites =array(); $ex_prev_sites =array(); @@ -598,6 +616,14 @@ class NDB_Form_User_Accounts extends NDB_Form array('multiple' => 'multiple') ); + $projectOptions = Utility::getProjectList(); + $this->addSelect( + 'ProjectIDs', + 'Projects', + $projectOptions, + array('multiple' => 'multiple') + ); + if ($editor->hasPermission('examiner_multisite')) { //get site aliases $DB =& Database::singleton(); diff --git a/modules/user_accounts/templates/form_edit_user.tpl b/modules/user_accounts/templates/form_edit_user.tpl index 3333b8a74f3..085163f969d 100644 --- a/modules/user_accounts/templates/form_edit_user.tpl +++ b/modules/user_accounts/templates/form_edit_user.tpl @@ -289,6 +289,14 @@ $(document).ready(function() { {/if} {/if} +
+ +
+ {$form.ProjectIDs.html} +
+