Skip to content

Commit 44617e4

Browse files
author
Eric Neuhaus
committed
Added XML file import for create project requests
1 parent 0fc3c6b commit 44617e4

File tree

4 files changed

+159
-32
lines changed

4 files changed

+159
-32
lines changed

QuickProjects.js

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
$(document).ready(function () {
2+
$('#user').autocomplete({
3+
source: app_path_webroot + "UserRights/search_user.php",
4+
minLength: 2,
5+
delay: 150,
6+
html: true,
7+
select: function (event, ui) {
8+
$(this).val(ui.item.value);
9+
return false;
10+
}
11+
});
12+
13+
$[ "ui" ][ "autocomplete" ].prototype["_renderItem"] = function( ul, item) {
14+
return $( "<li></li>" )
15+
.data( "item.autocomplete", item )
16+
.append( $( "<a></a>" ).html( item.label ) )
17+
.appendTo( ul );
18+
};
19+
20+
$('#storedXml').change(function () {
21+
var projectToken = $('#templateProjectToken');
22+
23+
if (this.checked) {
24+
projectToken.hide();
25+
}
26+
else {
27+
projectToken.show();
28+
}
29+
});
30+
});
31+
132
var UIOWA_QuickProjects = {};
233

334
UIOWA_QuickProjects.addService = function() {
@@ -33,6 +64,8 @@ UIOWA_QuickProjects.updateUrlText = function() {
3364
var paramStr = '';
3465
var paramList = [
3566
'token',
67+
'projectToken',
68+
'storedXml',
3669
'flag',
3770
'title',
3871
'irb',
@@ -52,14 +85,6 @@ UIOWA_QuickProjects.updateUrlText = function() {
5285
var projectSetup = document.querySelector('input[name = "setupMethod"]:checked').value;
5386
paramStr += '&method=' + projectSetup;
5487

55-
if (projectSetup == 'create') {
56-
document.querySelector('#projectLink').checked = true;
57-
document.querySelector('#publicSurveyLink').disabled = true;
58-
}
59-
else {
60-
document.querySelector('#publicSurveyLink').disabled = false;
61-
}
62-
6388
var returnValue = document.querySelector('input[name = "returnValue"]:checked').value;
6489
paramStr += '&return=' + returnValue;
6590

@@ -89,9 +114,7 @@ UIOWA_QuickProjects.updateUrlText = function() {
89114

90115
var notifyValue = projectInfo[j].children[1].children['surveyNotification'].children[0].checked;
91116

92-
if (projectSetup == 'modify') {
93-
paramStr += '&surveyNotification[]=' + notifyValue;
94-
}
117+
paramStr += '&surveyNotification[]=' + notifyValue;
95118
}
96119
}
97120
else if (paramList[i] == 'purpose_other') {
@@ -114,7 +137,7 @@ UIOWA_QuickProjects.updateUrlText = function() {
114137
}
115138
}
116139
}
117-
else if (paramList[i] == 'surveys' || paramList[i] == 'longitudinal' || paramList[i] == 'autonumber') {
140+
else if (paramList[i] == 'surveys' || paramList[i] == 'longitudinal' || paramList[i] == 'autonumber' || paramList[i] == 'storedXml') {
118141
if (projectInfo[j].checked && projectSetup == 'create') {
119142
paramStr += '&' + paramList[i] + '=' + '1';
120143
}
@@ -155,31 +178,41 @@ UIOWA_QuickProjects.updateFields = function(reqToken) {
155178
var projectSetup = document.querySelector('input[name = "setupMethod"]:checked').value;
156179
var projectTitle = document.getElementById('app_title');
157180
var projectPurpose = document.getElementById('purpose');
158-
var superToken = document.getElementById('superToken');
181+
var superToken = $('#superToken');
159182
var reservedFlag = document.getElementById('reservedFlag');
160-
var surveyNotifications = $('input[name = "surveyNotification"]');
161183
var newProjectSettings = $('#newProjectSettings > input');
184+
var templateProjectToken = $('#templateProjectToken');
185+
var templateXml = $('#templateXml');
162186

163187
if (projectSetup == 'create') {
164188
projectTitle.required = true;
165189
projectPurpose.required = true;
166-
superToken.style = "";
190+
191+
if (!templateXml.hasClass('always-hide')) {
192+
superToken.show();
193+
}
167194

168195
newProjectSettings.attr("disabled", false);
169-
surveyNotifications.attr("disabled", true);
170196
reservedFlag.style.display = "none";
197+
templateProjectToken.show();
198+
199+
if (!templateXml.hasClass('always-hide')) {
200+
templateXml.show();
201+
}
171202
}
172203
else if (projectSetup == 'modify') {
173204
projectTitle.required = false;
174205
projectPurpose.required = false;
175206
reservedFlag.style = "";
176207

177208
newProjectSettings.attr("disabled", true);
178-
surveyNotifications.attr("disabled", false);
179209

180-
if (!reqToken[0]) {
181-
superToken.style.display = "none";
210+
if (!reqToken[0] || !superToken.hasClass('show-on-modify')) {
211+
superToken.hide();
182212
}
213+
214+
templateProjectToken.hide();
215+
templateXml.hide();
183216
}
184217
};
185218

QuickProjects.php

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,20 @@ public function getPermissionsPresets() {
5353
}
5454

5555
public function generateProject() {
56+
$protocol = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
57+
5658
session_start();
5759

60+
error_log('test');
61+
5862
global $conn;
5963
if (!isset($conn))
6064
{
6165
db_connect(false);
6266
}
6367

68+
error_log($_REQUEST['token']);
69+
6470
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
6571
self::returnResultMessage(["ERROR: The requested method is not implemented."], null);
6672
}
@@ -91,7 +97,7 @@ public function generateProject() {
9197
$apiRequired = false;
9298
}
9399

94-
if (db_num_rows($result) == 0 && $apiRequired) {
100+
if (db_num_rows($result) == 0 && $apiRequired && !$this->getSystemSetting('no-token-required')) {
95101
self::returnResultMessage(["ERROR: Invalid API Super Token"], null);
96102
}
97103

@@ -123,7 +129,12 @@ public function generateProject() {
123129
self::returnResultMessage(["ERROR: Project Title is missing. This parameter is required for project copies."], null);
124130
}
125131

126-
$supertoken = $_REQUEST['token'];
132+
if ($this->getSystemSetting('no-token-required')) {
133+
$supertoken = $this->getSystemSetting('super-api-token');
134+
}
135+
else {
136+
$supertoken = $_REQUEST['token'];
137+
}
127138

128139
$projectTitle = urldecode($_REQUEST['title']);
129140
$projectNote = urldecode($_REQUEST['note']);
@@ -175,9 +186,24 @@ public function generateProject() {
175186
'data' => ''
176187
);
177188

189+
$exportProjectXml = array(
190+
'token' => '',
191+
'content' => 'project_xml'
192+
);
193+
178194
if ($_REQUEST['method'] == 'create') {
179-
if ($_REQUEST['return'] == 'publicSurveyLink') {
180-
self::returnResultMessage(["ERROR: New projects cannot return public survey links. Project was not created."], null);
195+
if (isset($_REQUEST['projectToken'])) {
196+
$exportProjectXml['token'] = $_REQUEST['projectToken'];
197+
$projectXml = $this->redcapApiCall($exportProjectXml, false);
198+
199+
$createProject['odm'] = $projectXml;
200+
}
201+
202+
if ($_REQUEST['storedXml'] == 1) {
203+
$docId = self::getSystemSetting('template-xml-file');
204+
$filename = db_query('select stored_name from redcap_edocs_metadata where doc_id = ' . $docId);
205+
206+
$createProject['odm'] = file_get_contents(EDOC_PATH . db_fetch_assoc($filename)['stored_name']);
181207
}
182208

183209
$token = $this->redcapApiCall($createProject, false);
@@ -261,6 +287,22 @@ public function generateProject() {
261287

262288
$this->redcapApiCall($importUsers, false);
263289

290+
if ($reservedPID == null) {
291+
$result = db_query('select project_id from redcap_user_rights where api_token = "' . $token . '"');
292+
$reservedPID = db_fetch_assoc($result)['project_id'];
293+
}
294+
295+
if ($_REQUEST['method'] == 'create' && $_REQUEST['return'] == 'publicSurveyLink') {
296+
$result = db_query('select event_id from redcap_events_metadata
297+
left join redcap_events_arms on redcap_events_metadata.arm_id = redcap_events_arms.arm_id
298+
where project_id = ' . $reservedPID);
299+
$eventId = db_fetch_assoc($result)['event_id'];
300+
$result = db_query('select survey_id from redcap_surveys where project_id = ' . $reservedPID);
301+
$surveyId = db_fetch_assoc($result)['survey_id'];
302+
303+
\Survey::setHash($surveyId, null, $eventId, null, true);
304+
}
305+
264306
if (isset($_REQUEST['surveyNotification'])) {
265307
$notifications = $_REQUEST['surveyNotification'];
266308

@@ -290,7 +332,7 @@ public function generateProject() {
290332
}
291333

292334
if ($_REQUEST['return'] == 'publicSurveyLink') {
293-
$sql = "
335+
$sql = "
294336
SELECT s.project_id,s.form_name,s.title as survey_title
295337
,pr.app_title, p.hash
296338
FROM redcap_surveys s
@@ -320,7 +362,7 @@ public function generateProject() {
320362
self::returnResultMessage($successMsg, $urlString);
321363
}
322364
else {
323-
self::returnResultMessage(['No public survey link found. Is the template project configured properly? NOTE: The reserved project (PID: ' . $reservedPID . ') was still updated.'], null);
365+
self::returnResultMessage(['No public survey link found.'], null);
324366
}
325367

326368
}
@@ -332,7 +374,8 @@ public function generateProject() {
332374
$exportedProjectInfo = json_decode($exportedProjectInfo, true);
333375

334376
$urlString =
335-
sprintf("https://%s%sProjectSetup/index.php?pid=%d", // Project Setup page
377+
sprintf("%s%s%sProjectSetup/index.php?pid=%d", // Project Setup page
378+
$protocol,
336379
SERVER_NAME,
337380
APP_PATH_WEBROOT,
338381
$exportedProjectInfo['project_id']);
@@ -402,14 +445,36 @@ public function displayRequestBuilderPage() {
402445
<label for="publicSurveyLink"> Public Survey Link</label>
403446
</td>
404447
</tr>
405-
<tr valign="top" id="superToken">
448+
<tr valign="top" id="superToken" style="display:none" class="<?= $this->getSystemSetting('require-super-token') ? 'show-on-modify ' : '' ?><?= $this->getSystemSetting('no-token-required') ? 'always-hide ' : '' ?>">
406449
<td style="padding-right:20px;width:150px;">
407450
<b>Super API token: </b>
408451
</td>
409452
<td>
410453
<input id="token" type="text" name="token" style="width:70%;max-width:450px;" required value="<?= $superApiToken ?>">
411454
</td>
412455
</tr>
456+
<tr>
457+
<td><br/></td>
458+
</tr>
459+
<tr valign="top" id="templateXml" style="padding-right:20px;width:150px;<?= !$this->getSystemSetting('template-xml-file') ? 'display:none;" class="always-hide' : '' ?>">
460+
<td style="padding-right:20px;width:150px;">
461+
<b>Use stored project XML template: </b>
462+
</td>
463+
<td>
464+
<input type="checkbox" name="storedXml" id="storedXml" onclick="UIOWA_QuickProjects.updateUrlText()">
465+
</td>
466+
</tr>
467+
<tr>
468+
<td><br/></td>
469+
</tr>
470+
<tr valign="top" id="templateProjectToken">
471+
<td style="padding-right:20px;width:150px;">
472+
<b>Source project API token (optional): </b>
473+
</td>
474+
<td>
475+
<input id="projectToken" type="text" name="projectToken" style="width:70%;max-width:450px;">
476+
</td>
477+
</tr>
413478
<tr valign="top" id="reservedFlag" style="display: none;">
414479
<td style="padding-right:20px;width:150px;">
415480
<b>Reserved flag: </b>

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,33 @@
11
## Quick Projects
22

33
### Description
4-
The Quick Projects module allows for the quick creation or modification of REDCap projects (including the process of adding users and assigning rights) via a simple user interface or POST request. Project creations are achieved through a series of REDCap API calls, while project modifications are performed against a pool of pre-configured "reserve" projects, allowing for more complicated project setups to be duplicated quickly. After the project is created/modified, a link to the project/public survey will be returned.
4+
The Quick Projects module allows for the quick creation or modification of REDCap projects (including the process of adding users and assigning rights) via a single POST request. Project creations are achieved through a series of REDCap API calls, while project modifications are performed against a pool of pre-configured "reserve" projects. After the project is created/modified, a link to the project/public survey will be returned.
55

66
### Basic Usage
7-
After downloading and enabling this module on your REDCap instance, a link to Quick Projects will appear at the bottom of the Control Center sidebar. This interface will allow you to configure your create/modify request via a graphical interface and execute immediately or copy the URL as a template for automation in another system.
7+
After downloading and enabling this module on your REDCap instance, a link to Quick Projects will appear at the bottom of the Control Center sidebar. This page will allow you to configure your create/modify request via a graphical interface and execute immediately or copy the URL as a template for automation in another system.
88

99
If the Quick Permissions module is also installed, any custom user rights presets configured will be available for use in Quick Projects as well.
1010

11+
### Create Project Method
12+
A Super API token is required to create projects.
13+
14+
Another project can be used as a template by either specifying a "Source project API token" or using a "stored project XML template". If an API token is specified, the source project's design will automatically be imported into the newly created project. If the XML template is used, the module will import the stored XML file (uploaded via module config) into the newly created project.
15+
16+
NOTE: REDCap v8.10.0 allows a number of additional project components (such as survey settings and reports) to be optionally included when downloading a project's metadata in XML format. However, none of these additional components will be included when using the "Source project API token" method. Please keep this in mind when deciding which of these features to use.
17+
1118
### Modify Project Method
1219
No additional configuration is required to create projects with this module, but performing a modify operation requires a bit of setup before use.
1320

1421
To use the modify project feature, you must have a "reserve" of template projects. These projects are designated by a specific project note (defined in module configuration) and every time a project is modified via Quick Projects, the project note will be erased (or replaced), removing it from the reserve. Additionally, the user requesting the modification must have an API token attached to the reserved project.
1522

23+
NOTE: This feature was originally implemented as a workaround for the lack of a "deep copy" method in REDCap. With the new functionality added in REDCap v8.10.0 (see note under "Create Project Method" section), this feature may no longer be necessary.
24+
1625
### Configuration Options
1726
* **Prepopulate Super API Token based on currently logged in user:** Check this option to automatically populate the Super API Token field if the current user has one.
1827
* **Prepopulate Super API Token with specified value:** If this field is not blank, the value entered will automatically populate the Super API Token field (regardless of the above option)
28+
* **Automatically use above token if not specified in request parameter:** If no super API token is passed with a request, the module will assume the token in the field above is to be used. WARNING: Enable at your own risk, as this allows anyone to create projects without authentication by submitting a request. It is recommended that the IP whitelisting feature is used in conjunction with this option for additional security.
1929
* **Only allow requests from whitelisted IP addresses:** If this option is enabled, Quick Projects will immediately return an error message to requests from IP addresses that are not explicitly whitelisted.
30+
* **Stored project metadata file:** An XML file containing project metadata (in CDISC ODM format) can be uploaded and optionally imported into projects created with this module.
2031

2132
#### Modify Project Settings
2233

config.json

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "Quick Projects",
3-
"description": "Create/modify projects via single POST request",
3+
"description": "Create/modify projects via a single POST request",
44

55
"namespace": "UIOWA\\QuickProjects",
66

@@ -29,16 +29,27 @@
2929
"system-settings": [
3030
{
3131
"key": "prepopulate-token",
32-
"name": "Prepopulate Super API Token based on currently logged in user",
32+
"name": "Prepopulate Super API Token based on currently logged in user (Request Builder)",
3333
"type": "checkbox",
3434
"required": false
3535
},
3636
{
3737
"key": "super-api-token",
38-
"name": "Prepopulate Super API Token with specified value (overrides above option)",
38+
"name": "Prepopulate Super API Token with specified value (Request Builder)",
3939
"type": "text",
4040
"required": false
4141
},
42+
{
43+
"key": "no-token-required",
44+
"name": "Automatically use above token if not specified in request parameter",
45+
"type": "checkbox",
46+
"required": false,
47+
"branchingLogic": {
48+
"field": "super-api-token",
49+
"op": "<>",
50+
"value": ""
51+
}
52+
},
4253
{
4354
"key": "restrict-ip",
4455
"name": "Only allow requests from whitelisted IP addresses",
@@ -56,6 +67,13 @@
5667
"value": "true"
5768
}
5869
},
70+
{
71+
"key": "template-xml-file",
72+
"name": "Stored project metadata file (XML in CDISC ODM format)",
73+
"type": "file",
74+
"required": false,
75+
"repeatable": false
76+
},
5977
{
6078
"key": "modify-project-settings",
6179
"name": "Modify Project Settings",

0 commit comments

Comments
 (0)