Skip to content

Commit

Permalink
Release 1.1.0: Enable DAG switch via API, disable config for superusers
Browse files Browse the repository at this point in the history
New functionality for release 1.1.0:
* Enable DAG switch via API
* Diisable DAG-user mapping config for superusers (it is not applicable because superusers always see all records)

Bug fix:
* JSON encode quotes in DAG names when rwiting to var in User Rights page
  • Loading branch information
Luke Stevens committed Sep 4, 2018
1 parent 5f8e52e commit 90191ae
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 16 deletions.
89 changes: 80 additions & 9 deletions DAGSwitcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
namespace MCRI\DAGSwitcher;

use ExternalModules\AbstractExternalModule;
use \ExternalModules\ExternalModules;
use Logging;
use RCView;
use Project;
use REDCap;

/**
Expand All @@ -33,12 +35,12 @@ class DAGSwitcher extends AbstractExternalModule
public function __construct() {
parent::__construct();
global $lang, $user_rights;
$this->lang = $lang;
$this->lang = &$lang;
$this->page = PAGE;
$this->project_id = intval(PROJECT_ID);
$this->super_user = SUPER_USER;
$this->user = strtolower(USERID);
$this->user_rights = $user_rights;
$this->user_rights = &$user_rights;
}

public function hook_every_page_top($project_id) {
Expand Down Expand Up @@ -187,13 +189,15 @@ protected function includeDAGPageJs() {
*/
public function getUserDAGsTable($rowsAreDags=true) {
$html = '';
$superusers = array();

if ($rowsAreDags) { // columns are users
// column-per-user, row-per-dag (load via ajax)
$col0Hdr = $this->lang['global_22']; // Data Access Groups
$colGroupHdr = $this->lang['control_center_132']; // Users
$colSet = REDCap::getUsers();
$this->setUserSetting('rowoption', 'dags');
$superusers = $this->readSuperUserNames();
} else { // $rowsAreDags===false // columns are dags
// column-per-dag, row-per-user (load via ajax)
$col0Hdr = $this->lang['control_center_132']; // Users
Expand All @@ -217,6 +221,9 @@ public function getUserDAGsTable($rowsAreDags=true) {
)
);
foreach ($colSet as $col) {
if ($rowsAreDags && in_array($col, $superusers)) {
$col = RCView::span(array('style'=>'color:#777;','title'=>'Super users see all!'),$col);
}
$colhdrs .= RCView::th(array('class'=>'', 'style'=>'border-top: 0px none; font-size: 12px; text-align: center; padding: 3px; white-space: normal; vertical-align: bottom; width: 22px;'),
RCView::div(array('style'=>'font-weight:normal;'),
RCView::span(array('class'=>'vertical-text'),
Expand Down Expand Up @@ -270,22 +277,24 @@ public function getUserDAGsTableRowData($rowsAreDags=true) {
'dagid' => $dagId,
'dagname' => $dagName,
'user' => $user,
'enabled' => (in_array($dagId, $usersEnabledDags[$user]))?1:0
'enabled' => (in_array($dagId, $usersEnabledDags[$user]))?1:0,
'is_super' => (in_array($user, $this->readSuperUserNames()))?1:0
);
}
$rows[] = $row;
}
} else {
foreach ($users as $user) {
$row = array();
$row[] = array('rowref'=>$user);
$row[] = array('rowref'=>$user,'is_super' => (in_array($user, $this->readSuperUserNames()))?1:0);
foreach ($dags as $dagId => $dagName) {
$row[] = array(
'rowref' => $user,
'dagid' => $dagId,
'dagname' => $dagName,
'user' => $user,
'enabled' => (in_array($dagId, $usersEnabledDags[$user]))?1:0
'enabled' => (in_array($dagId, $usersEnabledDags[$user]))?1:0,
'is_super' => (in_array($user, $this->readSuperUserNames()))?1:0
);
}
$rows[] = $row;
Expand All @@ -294,6 +303,22 @@ public function getUserDAGsTableRowData($rowsAreDags=true) {
return $rows;
}

/**
* Read the list of superusers' usernames
* @return array Usernames of superusers
*/
protected function readSuperUserNames() {
$superusers = array();

$r = db_query('select username from redcap_user_information where super_user=1');
if ($r->num_rows > 0) {
while ($row = $r->fetch_assoc()) {
$superusers[] = $row['username'];
}
}
return $superusers;
}

/**
* Enable or disable a DAG for a user
* @param int $user Valid username for project
Expand Down Expand Up @@ -368,10 +393,28 @@ protected function renderUserDAGInfo() {

$dagSelect = RCView::select(array('id'=>'dag-switcher-change-select', 'class'=>'form-control'), $thisUserOtherDags);

$apiMsg = '';
if ($this->user_rights['api_export'] || $this->user_rights['api_export']) {
$apiMsg = RCView::div(
array('class'=>'blue', 'style'=>'margin:5px 0;'),
RCView::img(array('src'=>'computer.png')).
RCView::span(array(), 'You may also use the <strong>API</strong>. ').
RCView::a(
array('href'=>'javascript:', 'onclick'=>'if($("#dag-switch-api-info").is(":hidden")){$(this).html("[show less]");$("#dag-switch-api-info").slideDown();}else{$(this).html("[show more]");$("#dag-switch-api-info").slideUp();};'),
RCView::span(array(), '[show info]')
).
RCView::div(
array('id'=>'dag-switch-api-info', 'style'=>'display:none;margin-top:10px;'),
'<strong>POST</strong> "token" and "dag" (id or unique name) to your API endpoint.<br>You must including the query string shown in this example:<br><span style="font-family:monospace;font-size:80%;">curl -d "token=ABCDEF0123456789ABCDEF0123456789&dag=site_a" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"'.APP_PATH_WEBROOT_FULL.'/api/?NOAUTH&type=module&prefix=dag_switcher&page=user_dag_switch_api"</span>'
)
);
}

print RCView::div(
array('id'=>'dag-switcher-change-dialog'),
RCView::div(array('style'=>'margin:5px 0;'), $dagSwitchDialogText).
$dagSelect
$dagSelect.
$apiMsg
);

print RCView::div(
Expand Down Expand Up @@ -416,15 +459,26 @@ protected function includeProjectPageJs() {

/**
* Switch current user to the dag id provided
* @param string $newDag
* @param string $newDag id or unique name of DAG to switch to
* @return string New DAG id on successful switch, error message on fail
*/
public function switchToDAG($newDag) {

$userDags = $this->getUserDAGs();

$projDags = array(0=>$this->lang['data_access_groups_ajax_23']) + (array)REDCap::getGroupNames();
$projDags = array(0=>$this->lang['data_access_groups_ajax_23']) + (array)REDCap::getGroupNames(true);

if (!is_int($newDag)) {
foreach ($projDags as $id => $uname) {
if ($uname===$newDag) {
$newDag = $id;
break;
}
}
}

if (!array_key_exists($newDag, $projDags)) { return 'Invalid DAG'; }

if (!array_key_exists($this->user, $userDags)) { return 'Invalid user'; }
if (!in_array($newDag, $userDags[$this->user])) { return 'User/DAG assignment not permitted'; }

Expand Down Expand Up @@ -462,7 +516,7 @@ protected function includeUserRightsPageJs() {
<script type="text/javascript">
$(document).ready(function() {
var userDags = JSON.parse('<?php echo json_encode($userDags);?>');
var dagNames = JSON.parse('<?php echo json_encode($dagNames);?>');
var dagNames = JSON.parse('<?php echo json_encode($dagNames, JSON_HEX_APOS);?>');
MCRI_DAG_Switcher_User_Rights.makePopovers(userDags, dagNames);
});

Expand Down Expand Up @@ -528,4 +582,21 @@ public function removeUserSetting($key)
{
\UIState::removeUIStateValue($this->project_id, self::UI_STATE_OBJECT_PREFIX . $this->PREFIX, $key);
}

public function apiDagSwitch($RestUtility, $newDag) {
global $Proj;

// look up token and set project context
$request = $RestUtility::processRequest(true);

$this->user = $request->getRequestVars()['username'];
$this->project_id = $request->getRequestVars()['projectid'];
$Proj = $this->Proj = new Project($this->project_id);

if(!ExternalModules::getProjectSetting($this->PREFIX, $this->project_id, ExternalModules::KEY_ENABLED)) {
return "The requested module is currently disabled on this project.";
}

return $this->switchToDAG($newDag);
}
}
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,24 @@ Luke Stevens, Murdoch Children's Research Institute https://www.mcri.edu.au
Enable project users to switch between any number of Data Access Groups (and/or
"No Assignment"):
- Adds a table of users/DAGs to the DAGs page so users with DAG permission may
enable and disable specific DAGs for each project user.
enable and disable specific DAGs for each project user (except super users).
- For users with multiple DAGs enabled, adds a display of a user's current DAG
assignment in a box at the top of pages where records may be viewed (e.g.
Dashboard, Export, Import, but not Project Home or Setup, or where a specific
record is selected: Record Home or an Instrument).
- Next to the display of the user's current DAG is a button that opens a dialog
where the user may select an alternative DAG from those that are enabled for
them (not available when a specific record is selected).
Nb. Super users always see all records: DAG switching is not applicable.
- The User Rights page indicates alongside each user's current DAG where other
DAGs are enabled for the user.
- Enables user with DAG permission to remove him or herself from a DAG, which
is not possible using standard REDCap functionality.

is not possible using standard REDCap functionality.
- From v1.1.0 it is possible to switch DAGs using an API call. Post 'token' and
'dag' (id or unique name) to your regular system API endpoint, using the
following query string:
?NOAUTH&type=module&prefix=dag_switcher&page=user_dag_switch_api
E.g.
curl -d "token=ABCDEF0123456789ABCDEF0123456789&dag=site_a"
"https://redcap.ourplace.edu/api/?NOAUTH&type=module&prefix=dag_switcher&page=user_dag_switch_api"
********************************************************************************
6 changes: 5 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@

"description": "Enable project users to switch between any number of Data Access Groups (and/or \"No Assignment\").",

"links": {
"no-auth-pages": [
"user_dag_switch_api"
],

"links": {
},

"system-settings": [
Expand Down
1 change: 1 addition & 0 deletions dag_switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var MCRI_DAG_Switcher_Switch = (function(window, document, $, undefined) {
$('#dag-switcher-change-dialog').dialog({
title: dagSwitchDialogTitle,
autoOpen: false,
width: 500,
modal: true,
buttons: {
Ok: function() {
Expand Down
13 changes: 10 additions & 3 deletions dag_user_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,22 @@ var MCRI_DAG_Switcher_Config = (function(window, document, $, undefined) {
{
"targets": 0,
"render": function ( celldata, type, row ) {
return celldata.rowref;
var celltext;
if (typeof celldata.is_super !== 'undefined' && celldata.is_super) {
celltext = '<span style="color:#777;" title="Super users see all!">'+celldata.rowref+'</span>';
} else {
celltext = celldata.rowref;
}
return celltext;
}
},
{
"targets": "_all",
"render": function ( celldata, type, row ) {
var checked = (celldata.enabled!==undefined && celldata.enabled)?"checked":"";
var checked = (celldata.enabled!==undefined && celldata.enabled)?'checked':'';
var disabled = (celldata.is_super!==undefined && celldata.is_super)?'disabled title="Super users see all!"':'';
if (type==='display') {
return "<input type='checkbox' data-dag='"+celldata.dagid+"' data-user='"+celldata.user+"' "+checked+" title='"+celldata.dagname+" : "+celldata.user+"'></input><img src='"+app_path_images+"progress_circle.gif' style='display:none;'>";
return "<input type='checkbox' data-dag='"+celldata.dagid+"' data-user='"+celldata.user+"' "+checked+" "+disabled+" title='"+celldata.dagname+" : "+celldata.user+"'></input><img src='"+app_path_images+"progress_circle.gif' style='display:none;'>";
} else {
return celldata.enabled+'-'+celldata.rowref; // for sorting
}
Expand Down
17 changes: 17 additions & 0 deletions user_dag_switch_api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
/**
* REDCap External Module: DAG Switcher
* @author Luke Stevens, Murdoch Children's Research Institute
* Switch current user to DAG supplied
*/
error_reporting(0);
header("Content-Type: application/json");

try {
if (!isset($_POST['dag'])) { echo json_encode(array('result'=>'No dag supplied')); }
$result = $module->apiDagSwitch('RestUtility', $_POST['dag']);
} catch (Exception $ex) {
http_response_code(500);
$result = $ex->getMessage();
}
echo json_encode(array('result'=>$result));

0 comments on commit 90191ae

Please sign in to comment.