diff --git a/CHANGELOG.md b/CHANGELOG.md index c0b6daa1373..36e1e9291e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ core section.*** - ***When possible please provide the number of the pull request(s) containing the changes in the following format: PR #1234*** -## LORIS 26.0 (Release Date: ????-??-??) +## LORIS 26.0 (Release Date: 2024-06-13) ### Core #### Features - Add OpenID Connect authorization support to LORIS (PR #8255) @@ -26,7 +26,7 @@ changes in the following format: PR #1234*** - While proposing a project or editing a project in publications module, prevent indefinite "File to upload" fields from being added if files are browsed then cancelled (PR #9179) - Conflict resolver fixed when Test_name is not equal to table name. This is done be replacing the "TableName" variable with "TestName" everywhere in resolved & unresolved conflicts tables as well as modules (PR #9270) -## LORIS 25.0 (Release Date: ????-??-??) +## LORIS 25.0 (Release Date: 2023-07-17) ### Core #### Features - Added new interface intended to be used for querying module data from PHP (PR #8215) diff --git a/README.md b/README.md index cba522a0958..34cc39dce4c 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ LORIS (Longitudinal Online Research and Imaging System) is a self-hosted web app * Try the LORIS demo instance at https://demo.loris.ca. -This Readme covers installation of LORIS version 25.0 on Ubuntu. +This Readme covers installation of LORIS version 26.0 on Ubuntu. ([CentOS Readme also available](docs/wiki/00_SERVER_INSTALL_AND_CONFIGURATION/01_LORIS_Install/CentOS/README.md)). diff --git a/docs/wiki/00_SERVER_INSTALL_AND_CONFIGURATION/01_LORIS_Install/CentOS/README.md b/docs/wiki/00_SERVER_INSTALL_AND_CONFIGURATION/01_LORIS_Install/CentOS/README.md index 5f4f132ebe2..c77ecdbf548 100644 --- a/docs/wiki/00_SERVER_INSTALL_AND_CONFIGURATION/01_LORIS_Install/CentOS/README.md +++ b/docs/wiki/00_SERVER_INSTALL_AND_CONFIGURATION/01_LORIS_Install/CentOS/README.md @@ -10,9 +10,9 @@ For further details on the install process, please see the LORIS GitHub Wiki Cen # System Requirements - Install dependencies Default dependencies installed by CentOS 7.x may not meet the version requirements for LORIS deployment or development: -* MariaDB 10.3 is supported for LORIS 25. +* MariaDB 10.3 is supported for LORIS 26. -* PHP 8.1 (or higher) is supported for LORIS 25. +* PHP 8.2 (or higher) is supported for LORIS 26. In addition to the above, the following packages should be installed with `yum` and may also differ from the packages referenced in the main (Ubuntu) [LORIS Readme](../../../../../README.md). Detailed command examples are provided below (`sudo` privilege may be required depending on your system). * Apache 2.4 or higher diff --git a/modules/api/php/endpoints/candidate/visit/image/qc.class.inc b/modules/api/php/endpoints/candidate/visit/image/qc.class.inc index ae8d3f2325d..a7299fd9f53 100644 --- a/modules/api/php/endpoints/candidate/visit/image/qc.class.inc +++ b/modules/api/php/endpoints/candidate/visit/image/qc.class.inc @@ -166,7 +166,7 @@ class Qc extends Endpoint implements \LORIS\Middleware\ETagCalculator ); } - $inputqcstatus = $data['QCStatus'] ?? null; + $inputqcstatus = $data['QC'] ?? null; $inputselected = $data['Selected'] ?? null; // TODO :: This is (and was) not checking or handling Caveats diff --git a/modules/candidate_parameters/jsx/DiagnosisEvolution.js b/modules/candidate_parameters/jsx/DiagnosisEvolution.js index eae7ad42535..a0a24261290 100644 --- a/modules/candidate_parameters/jsx/DiagnosisEvolution.js +++ b/modules/candidate_parameters/jsx/DiagnosisEvolution.js @@ -61,6 +61,7 @@ class DiagnosisEvolution extends Component { formattedDiagnosisEvolution() { const dxEvolution = this.state.data.diagnosisEvolution; let formattedDxEvolution = []; + try { dxEvolution.map((record) => { let formattedDiagnosis = []; Object.entries(JSON.parse(record.Diagnosis)).map((entry, index) => { @@ -94,6 +95,9 @@ class DiagnosisEvolution extends Component { ] ); }); + } catch (error) { + console.error('Error parsing JSON:', error); + } return formattedDxEvolution; } diff --git a/modules/electrophysiology_uploader/README.md b/modules/electrophysiology_uploader/README.md index 4dd9db07a52..9acba1cb6b1 100644 --- a/modules/electrophysiology_uploader/README.md +++ b/modules/electrophysiology_uploader/README.md @@ -2,7 +2,13 @@ ## Purpose -The electrophysiology uploader is intended to allow users to upload and browse electrophysiology files. +The electrophysiology uploader is intended to allow users to upload electrophysiology files. + +**LORIS 26 Beta Note:** Files uploaded in this module will not be viewable +in +the Electrophysiology Browser module. +This feature is under construction for the next release. Please get in +touch with the LORIS team to configure this for your project. ## Intended Users @@ -26,6 +32,7 @@ EEG recordings can be re-uploaded multiple times. Previous upload attempts will Permission `electrophysiology_browser_view_allsites` or `electrophysiology_browser_view_site` is necessary to have access to the module and gives the user the ability to upload and browse all recordings uploaded to the database. +Permission `monitor_eeg_uploads` is necessary to receive EEG upload notifications. #### Filesystem Permission @@ -49,3 +56,6 @@ EEGUploadIncomingPath - This setting determines where on the filesystem the `EEGUploadIncomingPath` following a successful archival and insertion through the LORIS-MRI pipeline. +### Caveat + +Archive extraction and EEG insertion have to be performed manually at the moment. \ No newline at end of file diff --git a/modules/electrophysiology_uploader/jsx/ElectrophysiologyUploader.js b/modules/electrophysiology_uploader/jsx/ElectrophysiologyUploader.js index 544b4cb743c..2dd53c040ec 100644 --- a/modules/electrophysiology_uploader/jsx/ElectrophysiologyUploader.js +++ b/modules/electrophysiology_uploader/jsx/ElectrophysiologyUploader.js @@ -56,19 +56,28 @@ export default function ElectrophysiologyUploader(props) { ]; return ( - - - - - - - - + <> +
+ LORIS 26 Beta Note: Files uploaded in this module + will not be viewable in the Electrophysiology Browser + module. This feature is under construction for the next release. + Please get in touch with the LORIS team to configure this for your + project. +
+ + + + + + + + + ); } diff --git a/modules/imaging_uploader/php/imaging_uploader.class.inc b/modules/imaging_uploader/php/imaging_uploader.class.inc index 984e982e4a7..d86ab8ac27e 100644 --- a/modules/imaging_uploader/php/imaging_uploader.class.inc +++ b/modules/imaging_uploader/php/imaging_uploader.class.inc @@ -125,7 +125,7 @@ class Imaging_Uploader extends \NDB_Menu_Filter_Form $upload_max_size = \Utility::getMaxUploadSize(); $error_message = "Please make sure files are not larger than " . $upload_max_size; - http_response_code(400); + http_response_code(413); $errors = [ 'IsPhantom' => [], 'candID' => [], diff --git a/modules/media/test/MediaTest.php b/modules/media/test/MediaTest.php index 4717729024a..96ebdae52c7 100644 --- a/modules/media/test/MediaTest.php +++ b/modules/media/test/MediaTest.php @@ -61,8 +61,41 @@ function testLoadsWithPermissionRead() "An error occured while loading the page.", $bodyText ); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("#dynamictable > thead > tr") + )->getText(); + $this->assertStringNotContainsString("Edit Metadata", $bodyText); + + $this->resetPermissions(); + } + /** + * Tests that the page does not load if the user does not have correct + * permissions + * + * @return void + */ + function testLoadsWithPermissionWrite() + { + $this->setupPermissions(["media_write"]); + $this->safeGet($this->url . "/media/"); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("body") + )->getText(); + $this->assertStringNotContainsString( + "You do not have access to this page.", + $bodyText + ); + $this->assertStringNotContainsString( + "An error occured while loading the page.", + $bodyText + ); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector("#dynamictable > thead > tr") + )->getText(); + $this->assertStringContainsString("Edit Metadata", $bodyText); $this->resetPermissions(); } + /** /** * Tests that the page does not load if the user does not have correct * permissions @@ -185,5 +218,45 @@ function _testFilter($element,$table,$records,$value) ); $this->assertEquals("", $inputText); } + /** + * Testing Browse tab and coulumn clicking + * + * @return void + */ + function testBrowseTab() + { + $this->safeGet($this->url . "/media/"); + + $this->checkColumn(2, "DCC090_V1_bmi.txt"); + $this->checkColumn(3, "DCC090"); + $this->checkColumn(4, "V1"); + $this->checkColumn(5, ""); + $this->checkColumn(6, ""); + $this->checkColumn(7, "Data Coordinating Center"); + $this->checkColumn(8, "Pumpernickel"); + } + /** + * Test Browse tab and coulumn clicking-middleware + * + * @param int $columnNumber columnNumber + * @param string $expectedText expectedText + * + * @return void + */ + function checkColumn($columnNumber, $expectedText) + { + $this->safeClick( + WebDriverBy::cssSelector( + "#dynamictable > thead > tr > th:nth-child($columnNumber)" + ) + ); + $bodyText = $this->safeFindElement( + WebDriverBy::cssSelector( + "#dynamictable > tbody > tr:nth-child(1)". + " > td:nth-child($columnNumber)" + ) + )->getText(); + $this->assertEquals($expectedText, $bodyText); + } } diff --git a/modules/media/test/TestPlan.md b/modules/media/test/TestPlan.md index 2609ae0cea7..d455af56702 100644 --- a/modules/media/test/TestPlan.md +++ b/modules/media/test/TestPlan.md @@ -4,7 +4,7 @@ Media module allows users to upload, browse and edit media files associated with a specific timepoint in Loris. -### 🔒 Permissions +### 🔒 Permissions [Automation Testing] In order to use the media module the user might need one or both of the following permissions: @@ -60,7 +60,7 @@ is selected, the file name should should start with [PSCID]\_[Visit Label]\_[Ins 7. Once the file finished uploading, a modal containing a success message should appear with an 'OK' button. 8. Click on the 👉 **OK** button and the page should refresh to the browse tab. Make sure the file you just uploaded is shown in the data table. -**Test file browsing** +**Test file browsing** [Automation Testing] 1. After a couple of files are uploaded, make sure they are properly displayed in the data table 2. Make sure that information in the data table corresponds to the information in the database (media table) 3. Click on 👉 **column headers** to make sure sorting functionality is working as expected (Ascending/Descending) diff --git a/modules/mri_violations/php/mri_violations.class.inc b/modules/mri_violations/php/mri_violations.class.inc index a5f162be3fc..4eb99ecdcdb 100644 --- a/modules/mri_violations/php/mri_violations.class.inc +++ b/modules/mri_violations/php/mri_violations.class.inc @@ -148,7 +148,11 @@ class Mri_Violations extends \DataFrameworkMenu ); default: return (new Provisioner()) - ->filter(new UserCenterMatchOrNull()) + ->filter( + new UserCenterMatchOrNullOrAnyPermission( + $this->allSitePermissionNames(), + ) + ) ->filter(new UserProjectMatchOrNull()); } } diff --git a/modules/statistics/php/charts.class.inc b/modules/statistics/php/charts.class.inc index 7620b0f4c66..29bc3985de3 100644 --- a/modules/statistics/php/charts.class.inc +++ b/modules/statistics/php/charts.class.inc @@ -127,7 +127,7 @@ class Charts extends \NDB_Page $recruitmentBySiteData[] = [ "label" => $siteName, - "total" => intval($data[$siteID]) ?? 0, + "total" => intval($data[$siteID] ?? 0), ]; } return new \LORIS\Http\Response\JsonResponse($recruitmentBySiteData); diff --git a/modules/statistics/php/statistics_dd_site.class.inc b/modules/statistics/php/statistics_dd_site.class.inc index ff28c8e93f0..91cf7e2e74f 100644 --- a/modules/statistics/php/statistics_dd_site.class.inc +++ b/modules/statistics/php/statistics_dd_site.class.inc @@ -112,7 +112,7 @@ class Statistics_DD_Site extends statistics_site function _getResults( ?\CenterID $centerID, ?\ProjectID $projectID, - $instrument + string $instrument ) { $this->_checkCriteria($centerID, $projectID); $DB = $this->loris->getDatabaseConnection(); diff --git a/modules/statistics/php/statistics_mri_site.class.inc b/modules/statistics/php/statistics_mri_site.class.inc index df2ff76682d..fddd484b404 100644 --- a/modules/statistics/php/statistics_mri_site.class.inc +++ b/modules/statistics/php/statistics_mri_site.class.inc @@ -79,7 +79,7 @@ class Statistics_Mri_Site extends Statistics_Site function _completeCount( ?\CenterID $centerID, ?\ProjectID $projectID, - $issue + string $issue ): ?string { return null; } @@ -96,7 +96,7 @@ class Statistics_Mri_Site extends Statistics_Site function _getResults( ?\CenterID $centerID, ?\ProjectID $projectID, - $issue + string $issue ) { $this->_checkCriteria($centerID, $projectID); $DB = $this->loris->getDatabaseConnection(); diff --git a/modules/statistics/php/statistics_site.class.inc b/modules/statistics/php/statistics_site.class.inc index 7c705cd7d98..1cfe732c91b 100644 --- a/modules/statistics/php/statistics_site.class.inc +++ b/modules/statistics/php/statistics_site.class.inc @@ -193,18 +193,20 @@ class Statistics_Site extends \NDB_Menu $this->_checkCriteria($centerID, $projectID); $DB = $this->loris->getDatabaseConnection(); $count = $DB->pselectOne( - "SELECT count(s.CandID) FROM session s, - candidate c, flag f, {$instrument} i - WHERE s.ID=f.SessionID AND f.CommentID=i.CommentID - AND s.CandID=c.CandID - AND s.Active='Y' - AND s.CenterID <> '1' - $this->query_criteria - AND f.Data_entry='Complete' - AND s.Current_stage <> 'Recycling Bin' - AND f.Administration='All' - AND i.CommentID NOT LIKE 'DDE%'", - $this->query_vars + "SELECT COUNT(DISTINCT s.CandID) FROM + flag f + JOIN session s ON (s.ID=f.SessionID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE + s.Active='Y' AND s.CenterID <> '1' + AND c.Active='Y' + $this->query_criteria + AND f.Data_entry='Complete' + AND s.Current_stage <> 'Recycling Bin' + AND f.Administration='All' + AND f.CommentID NOT LIKE 'DDE%' + AND f.Test_name=:instrument", + ['instrument' => $instrument, ...$this->query_vars] ); return $count; } @@ -218,24 +220,31 @@ class Statistics_Site extends \NDB_Menu * * @return array */ - function _getResults(?\CenterID $centerID, ?\ProjectID $projectID, $instrument) - { + function _getResults( + ?\CenterID $centerID, + ?\ProjectID $projectID, + string $instrument + ) { $this->_checkCriteria($centerID, $projectID); $DB = $this->loris->getDatabaseConnection(); $result = $DB->pselect( - "SELECT s.CandID, f.SessionID, i.CommentID, c.PSCID, + "SELECT DISTINCT s.CandID, + f.SessionID, + f.CommentID, + c.PSCID, s.Visit_label - FROM session s, candidate c, flag f, - {$instrument} i - WHERE s.ID=f.SessionID AND f.CommentID=i.CommentID - AND s.CandID=c.CandID - AND s.Active='Y' - AND s.CenterID <> '1' - AND s.Current_stage <> 'Recycling Bin' - $this->query_criteria - AND (f.Data_entry is NULL OR f.Data_entry<>'Complete') - AND i.CommentID NOT LIKE 'DDE%' ORDER BY s.Visit_label, c.PSCID", - $this->query_vars + FROM flag f + JOIN session s ON (s.ID=f.SessionID) + JOIN candidate c ON (c.CandID=s.CandID) + WHERE s.Active='Y' AND c.Active='Y' + AND s.CenterID <> '1' + AND s.Current_stage <> 'Recycling Bin' + $this->query_criteria + AND f.Test_name=:instrument + AND (f.Data_entry is NULL OR f.Data_entry<>'Complete') + AND f.CommentID NOT LIKE 'DDE%' + ORDER BY s.Visit_label, c.PSCID", + ['instrument'=>$instrument, ...$this->query_vars] ); return iterator_to_array($result); } diff --git a/raisinbread/test/api/LorisApiImagesTest.php b/raisinbread/test/api/LorisApiImagesTest.php index af068ac177a..576954b4043 100644 --- a/raisinbread/test/api/LorisApiImagesTest.php +++ b/raisinbread/test/api/LorisApiImagesTest.php @@ -137,7 +137,7 @@ public function testGetCandidatesCandidVisitImagesFilename(): void ), true ); - $this->assertEquals(null, $imagesArray); + $this->assertEquals(null, $imagesArray); } /** @@ -286,7 +286,7 @@ public function testPutCandidatesCandidVisitImagesFilenameQc(): void 'Visit' => $visit, 'File' => $filename ], - "QC" => 'pass', + "QC" => 'Pass', "Selected" => false, 'Caveats' => [ '0' => [ @@ -335,7 +335,7 @@ public function testGetCandidatesCandidVisitImagesFilenameFormatBbrowser(): void "candidates/$this->candidTest/$this->visitTest/images/" . "$this->imagefileTest/format/brainbrowser" ); - } + } $this->assertEquals(200, $response->getStatusCode()); // Verify the endpoint has a body $body = $response->getBody(); @@ -401,7 +401,7 @@ public function testGetCandidatesCandidVisitImagesFilenameFormatBbrowser(): void $this->assertArrayHasKey('zspace', $imagesArray); $this->assertArrayHasKey('space_length', $imagesArray['zspace']); $this->assertArrayHasKey('start', $imagesArray['zspace']); - $this->assertArrayHasKey('step', $imagesArray['zspace']); + $this->assertArrayHasKey('step', $imagesArray['zspace']); } /** diff --git a/raisinbread/test/api/LorisApiImages_v0_0_3_Test.php b/raisinbread/test/api/LorisApiImages_v0_0_3_Test.php index c6b65abde60..079a3e7be55 100644 --- a/raisinbread/test/api/LorisApiImages_v0_0_3_Test.php +++ b/raisinbread/test/api/LorisApiImages_v0_0_3_Test.php @@ -136,7 +136,7 @@ public function testGetCandidatesCandidVisitImagesFilename(): void ), true ); - $this->assertEquals(null, $imagesArray); + $this->assertEquals(null, $imagesArray); } /** @@ -285,7 +285,7 @@ public function testPutCandidatesCandidVisitImagesFilenameQc(): void 'Visit' => $visit, 'File' => $filename ], - "QC" => 'pass', + "QC" => 'Pass', "Selected" => false, 'Caveats' => [ '0' => [ @@ -334,7 +334,7 @@ public function testGetCandidatesCandidVisitImagesFilenameFormatBbrowser(): void "candidates/$this->candidTest/$this->visitTest/images/" . "$this->imagefileTest/format/brainbrowser" ); - } + } $this->assertEquals(200, $response->getStatusCode()); // Verify the endpoint has a body $body = $response->getBody(); @@ -400,7 +400,7 @@ public function testGetCandidatesCandidVisitImagesFilenameFormatBbrowser(): void $this->assertArrayHasKey('zspace', $imagesArray); $this->assertArrayHasKey('space_length', $imagesArray['zspace']); $this->assertArrayHasKey('start', $imagesArray['zspace']); - $this->assertArrayHasKey('step', $imagesArray['zspace']); + $this->assertArrayHasKey('step', $imagesArray['zspace']); } /** diff --git a/tools/generic_includes.php b/tools/generic_includes.php index 84f07fe6ac1..4edfc0d0f2e 100644 --- a/tools/generic_includes.php +++ b/tools/generic_includes.php @@ -24,6 +24,7 @@ $client->makeCommandLine(); $client->initialize($configFile); $config = NDB_Config::singleton(); +$DB = NDB_Factory::singleton()->database(); $lorisInstance = new \LORIS\LorisInstance( $DB, $config, @@ -32,4 +33,3 @@ __DIR__ . "/../modules/", ], ); -$DB = $lorisInstance->getDatabaseConnection();