diff --git a/gigadb/app/services/URLsService.php b/gigadb/app/services/URLsService.php index 65c8fedc3c..a6476ce87d 100644 --- a/gigadb/app/services/URLsService.php +++ b/gigadb/app/services/URLsService.php @@ -90,4 +90,58 @@ public function checkURLs(ClientInterface $webClient): array return $badUrls; } + + /** + * Manages query parameters for the current URL based on the current route and $_GET params. + * It allows for adding, modifying, and removing parameters in a single call. + * + * @param array $addOrModifyParams Associative array of parameters to add or overwrite (e.g., ['key' => 'value']). + * @param array $removeParams A simple array of parameter keys to remove from the URL (e.g., ['key1', 'key2']). + * @return string The newly generated URL. + */ + public static function editUrlQueryParams(array $addOrModifyParams = [], array $removeParams = []): string + { + foreach ($addOrModifyParams as $key => $value) { + if (!is_string($key) || preg_match('/[^\w.-]/', $key)) { + throw new \InvalidArgumentException('Invalid character in parameter key. Only word characters, dots, and hyphens are allowed.'); + } + if (!is_scalar($value) && !is_null($value)) { + throw new \InvalidArgumentException('Invalid value type. Only scalar values or null are allowed.'); + } + } + + foreach ($removeParams as $param) { + if (!is_string($param) || preg_match('/[^\w.-]/', $param)) { + throw new \InvalidArgumentException('Invalid character in parameter to remove. Only word characters, dots, and hyphens are allowed.'); + } + } + + $uri = Yii::app()->request->getRequestUri(); + $parts = parse_url($uri); + + $queryParams = []; + if (isset($parts['query']) && $parts['query'] !== '') { + parse_str($parts['query'], $queryParams); + } + + foreach ($removeParams as $param) { + unset($queryParams[$param]); + } + + foreach ($addOrModifyParams as $key => $value) { + $queryParams[$key] = $value; + } + + $newQuery = http_build_query($queryParams); + + $newUri = ($parts['path'] ?? ''); + if ($newQuery !== '') { + $newUri .= '?' . $newQuery; + } + if (isset($parts['fragment'])) { + $newUri .= '#' . $parts['fragment']; + } + + return $newUri; + } } diff --git a/less/index.less b/less/index.less index 0bd6db453a..87193ac5a4 100644 --- a/less/index.less +++ b/less/index.less @@ -75,3 +75,5 @@ @import "modules/carousel-slider.less"; @import "modules/news-section.less"; @import "modules/dataset-feed-section.less"; +@import "layout/print.less"; +@import "pages/dataset-print.less"; diff --git a/less/layout/print.less b/less/layout/print.less new file mode 100644 index 0000000000..62b9896fc3 --- /dev/null +++ b/less/layout/print.less @@ -0,0 +1,44 @@ +// print styles not specific to a module or page +body.print { + .print-hidden { + display: none!important; + } + + div { + display: block; + } + header, footer, nav, aside, video, iframe, .mobile-navigation, .skip-to-main-link { + display: none!important; + } + + [role="dialog"], [role="tooltip"], [role="tablist"] { + display: none!important; + } + [role="tabpanel"] { + display: block!important; + } + + .left-border-title { + all: unset; + } + + .left-border-title-lg { + all: unset; + } + + .badge { + all: unset; + } + + // hide hypothes.is widget + hypothesis-sidebar { + display: none!important; + } +} + + +body:not(.print) { + .print-only { + display: none!important; + } +} \ No newline at end of file diff --git a/less/pages/dataset-print.less b/less/pages/dataset-print.less new file mode 100644 index 0000000000..9da9f1e967 --- /dev/null +++ b/less/pages/dataset-print.less @@ -0,0 +1,204 @@ +body.print .dataset-view-container { + *, + *::before, + *::after { + background: transparent !important; + color: @color-true-black !important; + box-shadow: none !important; + text-shadow: none !important; + border-color: initial !important; + } + + font-size: 12pt; + line-height: 1.4; + + a, + a:visited { + text-decoration: underline; + } + + a:hover { + text-decoration: underline; + } + + a[href]:not(.print__collapse-href):after { + content: " (" attr(href) ")"; + font-size: 90%; + } + + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + :is(h1, h2, h3, h4, h5, h6, p, .subsection) { + margin-bottom: 1em; + } + + h1 { font-size: 24pt; } + h2 { font-size: 18pt; } + h3 { font-size: 14pt; } + + img { + display: none; + } + + .dataset-color-background-block { + padding: 0; + } + .result-sub-links { + text-wrap: wrap; + } + .media-left { + display: none; + } + .media-body { + width: auto; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + align-items: flex-start; + justify-content: flex-start; + gap: 0.5em; + } + + #dropdown-div { + float: none; + margin: 0; + + .drop-citation-btn { + display: none !important; + } + + .dropdown-box, + ul { + position: static; + border: none; + list-style: disc inside; + padding: 0; + margin: 0 0 0 20px; + } + + li { + a, + a:hover { + background: none !important; + color: initial !important; + padding: 0; + } + } + } + .citation-popup:before { + content: attr(data-content)": "; + } + .dataset-des-images { + display: none!important; + } + .content-popup { + display: none!important; + } + + .sort-message { + display: none!important; + } + + #samples_table_settings, #files_table_settings { + display: none!important; + } + + #\33 dmodels { + display: none!important; + } + + #\33 dsketchfab { + display: none!important; + } + + [role="tabpanel"]#protocolsio, + [role="tabpanel"]#jbrowse, + [role="tabpanel"]#codeocean { + display: none!important; + } + + table.table { + border: none; + border-top: 3px double @color-true-black; + border-bottom: 3px double @color-true-black; + padding: 5px 0; + thead { + display: none; + } + + tbody { + display: block; + border: none; + margin-top: 1em; + + tr { + border: none; + padding-bottom: 1em; + display: block; + &:not(:last-child) { + border-bottom: 1px solid @color-true-black; + margin-bottom: 1em; + } + + th, + td { + flex: 1 1 100%; + border: none; + display: block; + padding: 2px 0; + + &::before { + content: attr(data-header) ": "; + display: inline-block; + font-weight: 400; + width: 135px; + font-size: 10pt; + } + } + + [class*="js-short-"] { + display: none; + } + [class*="js-long-"] { + display: block!important; + } + [class*="js-long-"] + button { + display: none; + } + + .button-column { + display: none; + } + + > *:first-child { + flex: 0 0 40%; + font-weight: bold; + text-align: left; + } + + > *:not(:first-child) { + flex: 1 0 60%; + text-align: left; + word-break: break-word; + } + + &::after { + content: ""; + display: block; + width: 100%; + } + } + } + } + + .table-footer .pagination-wrapper { + display: none; + } +} + +body.print .sibling-dataset-links { + display: none; +} \ No newline at end of file diff --git a/less/pages/dataset.less b/less/pages/dataset.less index d15a111142..15ee2d06ac 100644 --- a/less/pages/dataset.less +++ b/less/pages/dataset.less @@ -1,167 +1,192 @@ /* dataset */ -.dataset-view-container { - margin-top: 40px; -} -.dataset-media { - margin-bottom: 20px; -} -.media-left { - padding-right: 20px; -} -.media-object { - max-width: none; - width: 300px; - height: auto; -} -p.dataset-release-date-text { - margin-bottom: 10px; - padding-left: 13px; -} -.dataset-color-background-block { - padding: 5px 10px 30px 10px; -} -.dataset .subsection { - margin-bottom: 20px; -} -.dataset .background-btn { - height: 44px; -} -.doi-badge .badge:last-child { - font-size: 13px; - font-weight: normal; - color: @color-true-white; - background-color: @color-gigadb-green; - padding: 5px 10px; - border-radius: 0px 4px 4px 0px; - text-decoration: none; -} -.doi-badge .badge:first-child { - background-color: @color-darker-gray; - font-size: 13px; - font-weight: normal; - padding: 5px 10px; - border-radius: 4px 0px 0px 4px; -} - -#dataset_slider { - h2 { - display: inline-block; - margin-right: 20px; - } - .carousel { - margin-bottom: 0; +body:not(.print) { + .dataset-view-container { + margin-top: 40px; + position: relative; + .print-button-container { + position: absolute; + top: 0; + right: 0; + } + @media (max-width: @screen-md-max) { + .print-button-container { + position: relative; + display: flex; + justify-content: flex-end; + } + } + @media (max-width: @screen-xs-max) { + .print-button-container { + margin-right: 22px; + } + } } - .carousel-inner { - width: auto; + .dataset-media { + margin-bottom: 20px; } - .data-block { - width: 172px; - float: left; - margin: 10px 0 10px 20px; - img { - width: auto; - margin: 0 auto 10px auto; - display: block; - max-width: 172px; - min-height: 172px; - height: 172; + @media (max-width: @screen-md-max) { + .dataset-media { + margin-top: 0; } } - .carousel-control.right { - right: 0px; - top: -18px; + .media-left { + padding-right: 20px; } - .carousel-control.left { - right: 25px; - top: -18px; - left: auto; + .media-object { + max-width: none; + width: 300px; + height: auto; } - .carousel-control.right, - .carousel-control.left { - font-size: 28px !important; - line-height: 18px; + p.dataset-release-date-text { + margin-bottom: 10px; + padding-left: 13px; + } + .dataset-color-background-block { + padding: 5px 10px 30px 10px; + } + .dataset .subsection { + margin-bottom: 20px; + } + .dataset .background-btn { + height: 44px; + } + .doi-badge .badge:last-child { + font-size: 13px; + font-weight: normal; + color: @color-true-white; + background-color: @color-gigadb-green; + padding: 5px 10px; + border-radius: 0px 4px 4px 0px; + text-decoration: none; + } + .doi-badge .badge:first-child { + background-color: @color-darker-gray; + font-size: 13px; + font-weight: normal; + padding: 5px 10px; + border-radius: 4px 0px 0px 4px; } -} -#dataset_slider .carousel-control.left { - font-size: 28px !important; - line-height: 18px; -} + #dataset_slider { + h2 { + display: inline-block; + margin-right: 20px; + } + .carousel { + margin-bottom: 0; + } + .carousel-inner { + width: auto; + } + .data-block { + width: 172px; + float: left; + margin: 10px 0 10px 20px; + img { + width: auto; + margin: 0 auto 10px auto; + display: block; + max-width: 172px; + min-height: 172px; + height: 172; + } + } + .carousel-control.right { + right: 0px; + top: -18px; + } + .carousel-control.left { + right: 25px; + top: -18px; + left: auto; + } + .carousel-control.right, + .carousel-control.left { + font-size: 28px !important; + line-height: 18px; + } + } -.left-border-title-lg i { - font-size: 15px; -} + #dataset_slider .carousel-control.left { + font-size: 28px !important; + line-height: 18px; + } -#dataset-block-wrapper { - width: 100%; - margin: 0; -} -/* Provides horizontal scrolling in dataset table */ -.dataset-datatables-wrapper { - overflow: auto; - width: 100%; - position: relative; -} -#badge-div { - margin-right: 10px; - float: left; -} -#dropdown-div { - margin: 0; - float: left; -} -.dropdown-box { - position: absolute; -} -.drop-citation-btn { - padding: 3px; - width: 110px; - color: @white-on-gigadb-green; - font-size: 13px; - font-weight: normal; - border: none; - cursor: pointer; - background-color: @color-gigadb-green; - border-radius: 4px 4px 4px 4px; - display: inline-block; - position: relative; - text-decoration: none; -} -#dropdown-div ul { - list-style: none; - position: relative; - float: none; - margin-top: 5px; - min-width: 100px; - border: 1px solid @color-gigadb-green; - border-radius: 4px 4px 4px 4px; - font-size: 13px; - cursor: pointer; -} -#dropdown-div li a:hover { - background-color: #f3faf6; -} + .left-border-title-lg i { + font-size: 15px; + } -.citation-list { - display: flex; - flex-direction: column; - gap: 12px; - color: @dark-gray-on-white; - line-height: 1.3; -} + #dataset-block-wrapper { + width: 100%; + margin: 0; + } + /* Provides horizontal scrolling in dataset table */ + .dataset-datatables-wrapper { + overflow: auto; + width: 100%; + position: relative; + } + #badge-div { + margin-right: 10px; + float: left; + } + #dropdown-div { + margin: 0; + float: left; + } + .dropdown-box { + position: absolute; + } + .drop-citation-btn { + padding: 3px; + width: 110px; + color: @white-on-gigadb-green; + font-size: 13px; + font-weight: normal; + border: none; + cursor: pointer; + background-color: @color-gigadb-green; + border-radius: 4px 4px 4px 4px; + display: inline-block; + position: relative; + text-decoration: none; + } + #dropdown-div ul { + list-style: none; + position: relative; + float: none; + margin-top: 5px; + min-width: 100px; + border: 1px solid @color-gigadb-green; + border-radius: 4px 4px 4px 4px; + font-size: 13px; + cursor: pointer; + } + #dropdown-div li a:hover { + background-color: #f3faf6; + } -@media (max-width: @screen-xs-max) { - .dataset-media { + .citation-list { display: flex; flex-direction: column; - align-items: center; - gap: 15px; - .dataset-media-left { - padding-right: 0; - } - .dataset-media-body { - width: initial; - display: block; + gap: 12px; + color: @dark-gray-on-white; + line-height: 1.3; + } + + @media (max-width: @screen-xs-max) { + .dataset-media { + display: flex; + flex-direction: column; + align-items: center; + gap: 15px; + .dataset-media-left { + padding-right: 0; + } + .dataset-media-body { + width: initial; + display: block; + } } } -} +} \ No newline at end of file diff --git a/protected/components/DatasetPageAssembly.php b/protected/components/DatasetPageAssembly.php index 9d0a84f7d6..7d4e4816b6 100644 --- a/protected/components/DatasetPageAssembly.php +++ b/protected/components/DatasetPageAssembly.php @@ -321,6 +321,16 @@ public function setDatasetFiles(int $pageSize, string $source): DatasetPageAssem } $pager = new FilesPagination(); + + // handle print view + $isPrint = Yii::app()->request->getParam('print') === 'true'; + $showFull = Yii::app()->request->getParam('full') === 'true'; + if ($isPrint && !$showFull) { + $pageSize = 100; + } elseif ($isPrint && $showFull) { + $pageSize = 10_000; // assumes 10k is well beyond the number of files any dataset has + } + $pager->setPageSize($pageSize); $this->_files = new FormattedDatasetFiles( $pager, @@ -365,6 +375,16 @@ public function setDatasetSamples(int $pageSize): DatasetPageAssembly } $pager = new FilesPagination(); + + // handle print view + $isPrint = Yii::app()->request->getParam('print') === 'true'; + $showFull = Yii::app()->request->getParam('full') === 'true'; + if ($isPrint && !$showFull) { + $pageSize = 100; + } elseif ($isPrint && $showFull) { + $pageSize = 10_000; // assumes 10k is well beyond the number of samples any dataset has + } + $pager->setPageSize($pageSize); $this->_samples = new FormattedDatasetSamples( $pager, diff --git a/protected/views/dataset/view.php b/protected/views/dataset/view.php index 9a851acd3a..518322c260 100755 --- a/protected/views/dataset/view.php +++ b/protected/views/dataset/view.php @@ -1,3 +1,7 @@ + + getDataProvider(); $sampleDataProvider = $samples->getDataProvider(); +$totalNbFiles = $fileDataProvider->getTotalItemCount(); +$totalNbSamples = $sampleDataProvider->getTotalItemCount(); ?> @@ -21,6 +27,9 @@
+
image) { @@ -320,19 +329,46 @@ function showText() {
+ request->getParam('print') === 'true'; + $showFull = Yii::app()->request->getParam('full') === 'true'; + + if ($totalNbFiles > 100 || $totalNbSamples > 100) { + if ($isPrint && !$showFull) { + $showAllUrl = URLsService::editUrlQueryParams(['full' => 'true']); + echo CHtml::tag( + 'p', + ['class' => 'print-only'], + CHtml::link( + 'Show all ' . ($totalNbFiles + $totalNbSamples) . ' results', + $showAllUrl, + ['class' => 'print__collapse-href'] + ) . + ' (they might take a while to load, depending on the number of results)' + ); + } else { + $showLessUrl = URLsService::editUrlQueryParams([], ['full']); + echo CHtml::tag( + 'p', + ['class' => 'print-only'], + CHtml::link('Show less results', $showLessUrl, ['class' => 'print__collapse-href']) + ); + } + } + ?> getTotalItemCount() > 0) { $samplesPerPage = $sampleDataProvider->getItemCount(); - $totalNbSamples = $sampleDataProvider->getTotalItemCount(); if (count($model->samples) > 0) { ?>
-

+

Click on a table column to sort the results.

Table Settings + @@ -349,12 +385,12 @@ function showText() { foreach ($sample_models as $sample) { ?> - - - - - - + + + + + + @@ -393,7 +429,6 @@ function showText() { getTotalItemCount() > 0) { $filesPerPage = $fileDataProvider->getItemCount(); - $totalNbFiles = $fileDataProvider->getTotalItemCount(); if (count($model->samples) > 0) { ?> @@ -401,12 +436,13 @@ function showText() {
-

+

Click on a table column to sort the results.

Table Settings

+
@@ -426,20 +462,20 @@ function showText() { foreach ($file_models as $file) { ?> - - - + + - - - - - + + + + +
"> @@ -482,7 +518,7 @@ function showText() { ?>
- +
@@ -497,10 +533,10 @@ function showText() { - - - - + + + + @@ -553,7 +589,7 @@ function showText() { ?>
- +
@@ -565,8 +601,8 @@ function showText() { getHistory() as $log) { ?> - - + + @@ -584,7 +620,7 @@ function showText() {
-
+ diff --git a/protected/views/layouts/main.php b/protected/views/layouts/main.php index 17b14ce9b0..22a81a0445 100644 --- a/protected/views/layouts/main.php +++ b/protected/views/layouts/main.php @@ -38,7 +38,7 @@ renderPartial('//shared/_matomo') ?> - + renderPartial('//shared/_header'); ?> diff --git a/protected/views/shared/_printToggle.php b/protected/views/shared/_printToggle.php new file mode 100644 index 0000000000..e184e3f555 --- /dev/null +++ b/protected/views/shared/_printToggle.php @@ -0,0 +1,52 @@ + element, and updates the + * button’s label accordingly. Pressing the button flips the state, modifies + * the URL via `history.replaceState`, and reapplies the class without + * reloading the page. + * + * Usage (Yii 1.1): + * renderPartial('shared/_printToggle'); ?> + * + * Requirements: + * - Pages should provide CSS rules that target `body.print`. + */ +?> + + diff --git a/tests/_support/AcceptanceTester.php b/tests/_support/AcceptanceTester.php index 8e1d14f9e6..51a689c622 100644 --- a/tests/_support/AcceptanceTester.php +++ b/tests/_support/AcceptanceTester.php @@ -451,4 +451,22 @@ public function iCannotSeeTheOptionSelectedFor($value, $id) { $this->dontSeeOptionIsSelected("#dataset-form select[id='$id']", $value); } + + /** + * @Then I should see header and footer + */ + public function iShouldSeeHeaderAndFooter() + { + $this->seeElement('header'); + $this->seeElement('footer'); + } + + /** + * @Then I should not see header and footer + */ + public function iShouldNotSeeHeaderAndFooter() + { + $this->dontSeeElement('header'); + $this->dontSeeElement('footer'); + } } diff --git a/tests/acceptance/DatasetView.feature b/tests/acceptance/DatasetView.feature index 7498a3aa5e..4b3ecfdc52 100644 --- a/tests/acceptance/DatasetView.feature +++ b/tests/acceptance/DatasetView.feature @@ -267,3 +267,55 @@ Feature: a user visit the dataset page Given I have not signed in When I am on "/dataset/100142" Then I should see "Read the pre-print publication(s):" + + @issue-152 @ok + Scenario: Show print view toggle button and switch to print view + Given I have not signed in + When I am on "/dataset/100006" + And I press the button "Print view" + Then I should be on "/dataset/100006?print=true" + And I should see "Web view" + And I should see "Currently in print view" + And I should not see header and footer + + @issue-152 @ok + Scenario: Show print view without header or footer + Given I have not signed in + When I am on "/dataset/100006?print=true" + Then I should see "Web view" + And I should see "Currently in print view" + And I should not see header and footer + + @issue-152 @ok + Scenario: Show full list of samples and files in print view + Given I have not signed in + When I am on "/dataset/100035?print=true" + And I follow "Show all 301 results" + And I wait "2" seconds + Then I should be on "/dataset/100035?print=true&full=true" + And I should see "Show less results" + + @issue-152 @ok + Scenario: Hide full list of samples and files in print view + Given I have not signed in + When I am on "/dataset/100035?print=true&full=true" + And I follow "Show less results" + And I wait "2" seconds + Then I should be on "/dataset/100035?print=true" + And I should see "Show all 301 results" + + @issue-152 @ok + Scenario: Show full list of samples and files from direct navigation + Given I have not signed in + When I am on "/dataset/100035?print=true&full=true" + Then I should see "Show less results" + + @issue-152 @ok + Scenario: From print view return to web view + Given I have not signed in + When I am on "/dataset/100006?print=true" + And I press the button "Web view" + Then I should be on "/dataset/100006" + And I should see "Print view" + And I should see "Currently in web view" + And I should see header and footer \ No newline at end of file