diff --git a/modules/api/php/endpoints/candidates.class.inc b/modules/api/php/endpoints/candidates.class.inc
index 64ae7d6c7e2..0cf6904debb 100644
--- a/modules/api/php/endpoints/candidates.class.inc
+++ b/modules/api/php/endpoints/candidates.class.inc
@@ -150,6 +150,10 @@ class Candidates extends Endpoint implements \LORIS\Middleware\ETagCalculator
$candidate = \NDB_Factory::singleton()->candidate($candID);
+ if (!$candidate->isAccessibleBy($user)) {
+ return new \LORIS\Http\Response\JSON\Forbidden();
+ }
+
$endpoint = new Candidate\Candidate($candidate);
$pathparts = array_slice($pathparts, 2);
@@ -241,8 +245,14 @@ class Candidates extends Endpoint implements \LORIS\Middleware\ETagCalculator
$pscid,
$project->getId()
);
- } catch (\LorisException | \InvalidArgumentException $e) {
- return new \LORIS\Http\Response\JSON\BadRequest($e->getMessage());
+ } catch (\ConflictException $e) {
+ return new \LORIS\Http\Response\JSON\Conflict(
+ $e->getMessage()
+ );
+ } catch (\Exception | \LorisException | \InvalidArgumentException $e) {
+ return new \LORIS\Http\Response\JSON\BadRequest(
+ $e->getMessage()
+ );
}
$candidate = \NDB_Factory::singleton()->candidate($candid);
diff --git a/modules/api/php/views/visit/flags.class.inc b/modules/api/php/views/visit/flags.class.inc
index e1d88c84c2d..fa92f8d6e12 100644
--- a/modules/api/php/views/visit/flags.class.inc
+++ b/modules/api/php/views/visit/flags.class.inc
@@ -50,9 +50,9 @@ class Flags
public function toArray(): array
{
$instrumentname = $this->_instrument->testName;
- $instrumentdata = $this->_instrument->getInstanceData();
+ $commentid = $this->_instrument->getCommentID() ?? '';
- $isDDE = strpos($instrumentdata['CommentID'], 'DDE_') === 0;
+ $isDDE = strpos($commentid, 'DDE_') === 0;
$meta = [
'Candidate' => $this->_timepoint->getCandID(),
diff --git a/modules/api/php/views/visit/instrument.class.inc b/modules/api/php/views/visit/instrument.class.inc
index f95f5f1f62d..3a764fd5081 100644
--- a/modules/api/php/views/visit/instrument.class.inc
+++ b/modules/api/php/views/visit/instrument.class.inc
@@ -51,8 +51,9 @@ class Instrument
{
$instrumentname = $this->_instrument->testName;
$instrumentdata = $this->_instrument->getInstanceData();
+ $commentid = $this->_instrument->getCommentID() ?? '';
- $isDDE = strpos($instrumentdata['CommentID'], 'DDE_') === 0;
+ $isDDE = strpos($commentid, 'DDE_') === 0;
$meta = [
'Candidate' => $this->_timepoint->getCandID(),
diff --git a/modules/battery_manager/php/testoptionsendpoint.class.inc b/modules/battery_manager/php/testoptionsendpoint.class.inc
index 4fa79395547..3d093bcd8f0 100644
--- a/modules/battery_manager/php/testoptionsendpoint.class.inc
+++ b/modules/battery_manager/php/testoptionsendpoint.class.inc
@@ -11,6 +11,7 @@
* @link https://www.github.com/aces/Loris/
*/
namespace LORIS\battery_manager;
+use LORIS\VisitController;
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Message\ResponseInterface;
@@ -58,11 +59,16 @@ class TestOptionsEndpoint extends \NDB_Page
*/
private function _getOptions() : array
{
+ $visitController = new VisitController(
+ $this->loris->getDatabaseConnection()
+ );
return [
- 'instruments' => \Utility::getAllInstruments(),
+ 'instruments' => \NDB_BVL_Instrument::getInstrumentNamesList(
+ $this->loris
+ ),
'stages' => $this->_getStageList(),
'subprojects' => \Utility::getSubprojectList(null),
- 'visits' => \Utility::getVisitList(),
+ 'visits' => $visitController->getVisitlabels(),
'sites' => \Utility::getSiteList(false),
'firstVisit' => $this->_getYesNoList(),
'active' => $this->_getYesNoList(),
diff --git a/modules/imaging_uploader/jsx/UploadForm.js b/modules/imaging_uploader/jsx/UploadForm.js
index 3b2fe0251a1..1561f009b20 100644
--- a/modules/imaging_uploader/jsx/UploadForm.js
+++ b/modules/imaging_uploader/jsx/UploadForm.js
@@ -66,10 +66,22 @@ class UploadForm extends Component {
let ids = patientName.split('_');
formData.candID = ids[1];
formData.pSCID = ids[0];
- // visitLabel can contain underscores
- // join the remaining elements of patientName and use as visitLabel
+ // visitLabel can contain underscores, filename can have suffix appended to PSCID_CandID_VisitLabel
+ // join the remaining elements of patientName and pattern match
+ // against each visit label. Use as visitLabel the best (longest) match
ids.splice(0, 2);
- formData.visitLabel = ids.join('_');
+ const suffix = ids.join('_');
+ const visitLabels = Object.keys(form.visitLabel.options);
+ let bestMatch = '';
+ visitLabels.map((visitLabel) => {
+ if (suffix.match(visitLabel) !== null) {
+ // consider the first match only
+ if (suffix.match(visitLabel)[0].length > bestMatch.length) {
+ bestMatch = suffix.match(visitLabel)[0];
+ }
+ }
+ });
+ formData.visitLabel = bestMatch;
}
}
@@ -81,10 +93,22 @@ class UploadForm extends Component {
let ids = patientName.split('_');
formData.candID = ids[1];
formData.pSCID = ids[0];
- // visitLabel can contain underscores
- // join the remaining elements of patientName and use as visitLabel
+ // visitLabel can contain underscores, filename can have suffix appended to PSCID_CandID_VisitLabel
+ // join the remaining elements of patientName and pattern match
+ // against each visit label. Use as visitLabel the best (longest) match
ids.splice(0, 2);
- formData.visitLabel = ids.join('_');
+ const suffix = ids.join('_');
+ const visitLabels = Object.keys(form.visitLabel.options);
+ let bestMatch = '';
+ visitLabels.map((visitLabel) => {
+ if (suffix.match(visitLabel) !== null) {
+ // consider the first match only
+ if (suffix.match(visitLabel)[0].length > bestMatch.length) {
+ bestMatch = suffix.match(visitLabel)[0];
+ }
+ }
+ });
+ formData.visitLabel = bestMatch;
}
}
diff --git a/modules/issue_tracker/php/edit.class.inc b/modules/issue_tracker/php/edit.class.inc
index 448dac4997b..0e3a0680d52 100644
--- a/modules/issue_tracker/php/edit.class.inc
+++ b/modules/issue_tracker/php/edit.class.inc
@@ -447,11 +447,11 @@ class Edit extends \NDB_Page implements ETagCalculator
$historyValues = $this->getChangedValues($issueValues, $issueID, $user);
if (!empty($issueID)) {
- $db->update('issues', $issueValues, ['issueID' => $issueID]);
+ $db->unsafeUpdate('issues', $issueValues, ['issueID' => $issueID]);
} else {
$issueValues['reporter'] = $user->getUsername();
$issueValues['dateCreated'] = date('Y-m-d H:i:s');
- $db->insert('issues', $issueValues);
+ $db->unsafeInsert('issues', $issueValues);
$issueID = intval($db->getLastInsertId());
}
@@ -815,7 +815,7 @@ class Edit extends \NDB_Page implements ETagCalculator
'issueID' => $issueID,
'addedBy' => $user->getUsername(),
];
- $db->insert('issues_history', $changedValues);
+ $db->unsafeInsert('issues_history', $changedValues);
}
}
}
@@ -838,7 +838,7 @@ class Edit extends \NDB_Page implements ETagCalculator
'addedBy' => $user->getUsername(),
'issueID' => $issueID,
];
- $db->insert('issues_comments', $commentValues);
+ $db->unsafeInsert('issues_comments', $commentValues);
}
}
diff --git a/modules/survey_accounts/js/survey_accounts_helper.js b/modules/survey_accounts/js/survey_accounts_helper.js
index 917e91eef37..ce3acbbad9b 100644
--- a/modules/survey_accounts/js/survey_accounts_helper.js
+++ b/modules/survey_accounts/js/survey_accounts_helper.js
@@ -16,11 +16,13 @@ $(document).ready(function () {
// Handles cases where there was an error on the page and we're resubmitting
var email2 = $("input[name=Email2]").val();
var email = $("input[name=Email]").val();
- if (email.length > 0 && email2.length > 0 && email == email2)
- {
- $('#email_survey').removeAttr('disabled');
- } else {
- $('#email_survey').attr('disabled','disabled');
+ if (email && email2) {
+ if (email.length > 0 && email2.length > 0 && email == email2)
+ {
+ $('#email_survey').removeAttr('disabled');
+ } else {
+ $('#email_survey').attr('disabled','disabled');
+ }
}
// Reset Test_name so that the template can be loaded by ajax below
$("select[name=Test_name]").val("");
@@ -93,7 +95,7 @@ $(document).ready(function () {
$("#emailContent").val(content);
}
);
-
+
});
});
diff --git a/modules/survey_accounts/jsx/surveyAccountsIndex.js b/modules/survey_accounts/jsx/surveyAccountsIndex.js
index 0fac30465de..7241e266873 100644
--- a/modules/survey_accounts/jsx/surveyAccountsIndex.js
+++ b/modules/survey_accounts/jsx/surveyAccountsIndex.js
@@ -112,7 +112,11 @@ class SurveyAccountsIndex extends Component {
options: options.instruments,
}},
{label: 'URL', show: true},
- {label: 'Status', show: true},
+ {label: 'Status', show: true, filter: {
+ name: 'Status',
+ type: 'select',
+ options: options.statusOptions,
+ }},
];
const addSurvey = () => {
location.href='/survey_accounts/addSurvey/';
diff --git a/modules/survey_accounts/php/addsurvey.class.inc b/modules/survey_accounts/php/addsurvey.class.inc
index 7bd74d60ae9..17e23330d55 100644
--- a/modules/survey_accounts/php/addsurvey.class.inc
+++ b/modules/survey_accounts/php/addsurvey.class.inc
@@ -162,8 +162,9 @@ class AddSurvey extends \NDB_Form
];
}
}
-
- if ($_REQUEST['fire_away'] !== 'Create survey') {
+ if (!isset($_REQUEST['fire_away'])
+ || ($_REQUEST['fire_away'] !== 'Create survey')
+ ) {
if (!filter_var(
$values['Email'],
FILTER_VALIDATE_EMAIL
@@ -241,11 +242,11 @@ class AddSurvey extends \NDB_Form
'CommentID' => $commentID,
]
);
+ $this->tpl_data['success'] = true;
} catch (\DatabaseException $e) {
error_log($e->getMessage());
$this->tpl_data['success'] = false;
}
- $this->tpl_data['success'] = true;
if ($email && ($values['send_email'] == 'true')) {
$config = \NDB_Config::singleton();
@@ -291,7 +292,7 @@ class AddSurvey extends \NDB_Form
"Instrument",
array_merge(
['' => ''],
- \Utility::getDirectInstruments()
+ \NDB_BVL_Instrument::getDirectEntryInstrumentNamesList($this->loris)
)
);
$this->addBasicText("Email", "Email address");
diff --git a/modules/survey_accounts/php/survey_accounts.class.inc b/modules/survey_accounts/php/survey_accounts.class.inc
index 9154921e4a1..7256ccc759e 100644
--- a/modules/survey_accounts/php/survey_accounts.class.inc
+++ b/modules/survey_accounts/php/survey_accounts.class.inc
@@ -74,14 +74,22 @@ class Survey_Accounts extends \DataFrameworkMenu
*/
public function getFieldOptions() : array
{
+ $statusOptions = [
+ 'Created' => 'Created',
+ 'Sent' => 'Sent',
+ 'In Progress' => 'In Progress',
+ 'Complete' => 'Complete',
+ ];
+
$instruments
= \NDB_BVL_Instrument::getDirectEntryInstrumentNamesList(
$this->loris
);
return [
- 'visits' => \Utility::getVisitList(),
- 'instruments' => $instruments,
+ 'visits' => \Utility::getVisitList(),
+ 'instruments' => $instruments,
+ 'statusOptions' => $statusOptions,
];
}
diff --git a/php/exceptions/ConflictException.class.inc b/php/exceptions/ConflictException.class.inc
new file mode 100644
index 00000000000..0c5ef1b0bfa
--- /dev/null
+++ b/php/exceptions/ConflictException.class.inc
@@ -0,0 +1,11 @@
+feedbackThread->getSummaryOfThreads();
$this->tpl_data['thread_summary_headers'] = json_encode($summary);
- $field_names = Utility::getSourcefields($_REQUEST['test_name'] ?? '');
+ $test_name = '';
+ if (array_key_exists('test_name', $_REQUEST)) {
+ $test_name = $_REQUEST['test_name'];
+ } else if (array_key_exists('lorispath', $_REQUEST)) {
+ $test_name = preg_split("#/#", $_REQUEST['lorispath'])[1] ?? '';
+ }
+
+ // Get field names
+ $field_names = Utility::getSourcefields($test_name);
$fields = [];
$fields['Across All Fields'] = 'Across All Fields';
foreach ($field_names as $field_name) {
diff --git a/php/libraries/Candidate.class.inc b/php/libraries/Candidate.class.inc
index cf987cf047b..cf71013f0e9 100644
--- a/php/libraries/Candidate.class.inc
+++ b/php/libraries/Candidate.class.inc
@@ -223,6 +223,7 @@ class Candidate implements \LORIS\StudyEntities\AccessibleResource,
ProjectID $registrationProjectID = null
): CandID {
$factory = NDB_Factory::singleton();
+ $db = $factory->database();
$site = \Site::singleton($centerID);
@@ -264,6 +265,25 @@ class Candidate implements \LORIS\StudyEntities\AccessibleResource,
);
}
+ // check pscid uniqueness
+ $existing = $db->pselectOne(
+ 'SELECT
+ COUNT(*)
+ FROM candidate
+ WHERE PSCID = :v_pscid
+ GROUP BY
+ PSCID
+ ',
+ ['v_pscid' => $PSCID]
+ );
+
+ if ($existing > 0) {
+ throw new \ConflictException(
+ "PSCID must be unique",
+ PSCID_NOT_UNIQUE
+ );
+ }
+
// check pscid structure
if (!Candidate::validatePSCID(
$PSCID,
diff --git a/php/libraries/LorisForm.class.inc b/php/libraries/LorisForm.class.inc
index bc29b94bc18..8a3211d37e8 100644
--- a/php/libraries/LorisForm.class.inc
+++ b/php/libraries/LorisForm.class.inc
@@ -1592,6 +1592,7 @@ class LorisForm
$checked = '';
$value = '';
$disabled = '';
+ $required = '';
if (!empty($val)) {
$checked = 'checked="checked"';
}
@@ -1601,6 +1602,9 @@ class LorisForm
if (isset($el['disabled']) || $this->frozen) {
$disabled = 'disabled';
}
+ if (isset($el['required'])) {
+ $required = 'required';
+ }
/// XXX: There seems to be a problem when using to separate the
// checkbox from the label. Both Firefox and Chrome will still put a
// linebreak between the space and the checkbox. Instead, we wrap use
@@ -1609,7 +1613,7 @@ class LorisForm
// label it's still allowed to have linebreaks.
return ""
+ . "$disabled $required/>"
. " $el[label]";
}
diff --git a/php/libraries/LorisFormDictionaryImpl.class.inc b/php/libraries/LorisFormDictionaryImpl.class.inc
index 9fae78137c8..f6290a60407 100644
--- a/php/libraries/LorisFormDictionaryImpl.class.inc
+++ b/php/libraries/LorisFormDictionaryImpl.class.inc
@@ -131,6 +131,7 @@ trait LorisFormDictionaryImpl
$t = new \LORIS\Data\Types\StringType(255);
break;
case 'header':
+ case 'hidden':
continue 2;
default:
throw new \LorisException(
diff --git a/php/libraries/NDB_BVL_Instrument_LINST.class.inc b/php/libraries/NDB_BVL_Instrument_LINST.class.inc
index 495843a4358..1c196e3753d 100644
--- a/php/libraries/NDB_BVL_Instrument_LINST.class.inc
+++ b/php/libraries/NDB_BVL_Instrument_LINST.class.inc
@@ -734,14 +734,14 @@ class NDB_BVL_Instrument_LINST extends \NDB_BVL_Instrument
case 'numeric':
if ($addElements) {
$this->addNumericElement($pieces[1], $pieces[2]);
- $this->dictionary[] = new DictionaryItem(
- $this->testName."_".$pieces[1],
- $pieces[2],
- $scope,
- new IntegerType(),
- new Cardinality(Cardinality::SINGLE),
- );
}
+ $this->dictionary[] = new DictionaryItem(
+ $this->testName."_".$pieces[1],
+ $pieces[2],
+ $scope,
+ new IntegerType(),
+ new Cardinality(Cardinality::SINGLE),
+ );
if ($firstFieldOfPage) {
$this->_requiredElements[] = $fieldname;
$firstFieldOfPage = false;
diff --git a/php/libraries/VisitController.class.inc b/php/libraries/VisitController.class.inc
index aec1ca84cd7..69a5563f28e 100644
--- a/php/libraries/VisitController.class.inc
+++ b/php/libraries/VisitController.class.inc
@@ -54,22 +54,31 @@ class VisitController
return array_map(
function ($row) {
return new \LORIS\Visit(
- $row['name'],
- $row['ID']
+ $row['VisitName'],
+ $row['VisitLabel']
);
},
$this->database->pselect(
- 'SELECT
- v.VisitID as "ID", v.VisitName as "name"
- FROM
- visit v
-ORDER BY ID
- ',
+ 'SELECT VisitName, VisitLabel FROM visit ORDER BY VisitName',
[]
)
);
}
+ /**
+ * Get an associative array of the type
+ *
+ * @return array
+ */
+ public function getVisitlabels(): array
+ {
+ $visitLabels = [];
+ foreach ($this->getAllVisits() as $visitObj) {
+ $visitLabels[$visitObj->getName()] = $visitObj->getLabel();
+ }
+ return $visitLabels;
+ }
+
/**
* Retruns a VisitID by name
*
diff --git a/test/unittests/LorisForms_Test.php b/test/unittests/LorisForms_Test.php
index 276616dbb56..61b269c357e 100644
--- a/test/unittests/LorisForms_Test.php
+++ b/test/unittests/LorisForms_Test.php
@@ -1336,7 +1336,7 @@ function testCheckboxHTMLWithNoAttributes()
$this->form->addCheckbox("abc", "Hello", []);
$this->assertEquals(
" Hello",
+ "type=\"checkbox\" /> Hello",
$this->form->checkboxHTML($this->form->form['abc'])
);
}
@@ -1358,7 +1358,7 @@ function testCheckboxHTMLWithAttributesSet()
$this->assertEquals(
" Hello",
+ " value=\"value1\" disabled /> Hello",
$this->form->checkboxHTML($this->form->form['abc'])
);
}
diff --git a/tools/CouchDB_MRI_Importer.php b/tools/CouchDB_MRI_Importer.php
index c58c28f73c5..a8d3dde87ec 100755
--- a/tools/CouchDB_MRI_Importer.php
+++ b/tools/CouchDB_MRI_Importer.php
@@ -249,7 +249,10 @@ function _addMRIHeaderInfo($FileObj, $scan_type)
$FileObj,
'acquisition_date'
);
- $header['FileInsertDate_'.$type] = $FileObj->getParameter('InsertTime');
+ $header['FileInsertDate_'.$type] = date(
+ 'Y-m-d',
+ $FileObj->getParameter('InsertTime')
+ );
$header['SeriesDescription_'.$type] = $FileObj->getParameter($ser_desc);
$header['SeriesNumber_'.$type] = $FileObj->getParameter($ser_num);
$header['EchoTime_'.$type] = number_format(
@@ -438,7 +441,7 @@ function updateCandidateDocs($data, $ScanTypes)
$row['PSCID'],
$row['Visit_label'],
];
- $docid = 'MRI_Files:' . join($identifier, '_');
+ $docid = 'MRI_Files:' . join('_', $identifier);
unset($doc['PSCID']);
unset($doc['Visit_label']);
unset($doc['SessionID']);