From 31520ddb278efeb4ea38aef9f08b2562c9c4bed9 Mon Sep 17 00:00:00 2001 From: jyhein <124268211+jyhein@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:55:31 +0200 Subject: [PATCH] pkp/pkp-lib#7272 Simultaneously Displaying Multilingual Metadata on the Article Landing Page --- locale/en/locale.po | 3 + pages/article/ArticleHandler.php | 42 +++++++++++- plugins/themes/default/DefaultThemePlugin.php | 20 ++++++ plugins/themes/default/locale/en/locale.po | 12 ++++ .../styles/objects/article_details.less | 17 +++++ .../frontend/objects/article_details.tpl | 68 +++++++++++++------ 6 files changed, 138 insertions(+), 24 deletions(-) diff --git a/locale/en/locale.po b/locale/en/locale.po index de3a28e991a..fc3eaa5cb35 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -168,6 +168,9 @@ msgstr "Submission ID" msgid "article.title" msgstr "Title" +msgid "article.titlesLanguages" +msgstr "Article titles in other languages" + msgid "article.authors" msgstr "Authors" diff --git a/pages/article/ArticleHandler.php b/pages/article/ArticleHandler.php index 07f6bfb8505..e611c0995a0 100644 --- a/pages/article/ArticleHandler.php +++ b/pages/article/ArticleHandler.php @@ -167,8 +167,8 @@ public function initialize($request, $args = []) $this->galley = $galley; break; - // In some cases, a URL to a galley may use the ID when it should use - // the urlPath. Redirect to the galley's correct URL. + // In some cases, a URL to a galley may use the ID when it should use + // the urlPath. Redirect to the galley's correct URL. } elseif (ctype_digit($galleyId) && $galley->getId() == $galleyId) { $request->redirect(null, $request->getRequestedPage(), $request->getRequestedOp(), [$submission->getBestId(), $galley->getBestGalleyId()]); } @@ -357,6 +357,8 @@ public function view($args, $request) $templateMgr->assign('purchaseArticleEnabled', true); } + $templateMgr->assign('pubLocaleData', $this->getPublicationLocaleData($publication, $context->getPrimaryLocale(), $article->getData('locale'), $templateMgr->getTemplateVars('activeTheme')->getOption('showMetadata') ?: [])); + if (!Hook::call('ArticleHandler::view', [&$request, &$issue, &$article, $publication])) { $templateMgr->display('frontend/pages/article.tpl'); event(new UsageEvent(Application::ASSOC_TYPE_SUBMISSION, $context, $article, null, null, $this->issue)); @@ -616,4 +618,40 @@ public function userCanViewGalley($request, $articleId, $galleyId = null) } return true; } + + /** + * For article details, format display data. + */ + protected function getPublicationLocaleData(\APP\publication\Publication $publication, string $contextPrimaryLocale, string $submissionLocale, array $metadataOpts): array + { + $titles = $publication->getTitles('html'); + $subtitles = $publication->getSubtitles('html'); + $primaryLocale = isset($titles[$contextPrimaryLocale]) ? $contextPrimaryLocale : $submissionLocale; + $uiLocale = $contextPrimaryLocale; + $getOtherTitles = fn (array $tt): array => in_array('titles', $metadataOpts) ? array_filter($tt, fn (string $locale) => $locale !== $primaryLocale, ARRAY_FILTER_USE_KEY) : []; + $getMdata = fn (string $opt): array => empty(count($mdata = array_filter($publication->getData($opt) ?? []))) || in_array($opt, $metadataOpts) + ? $mdata + : (isset($mdata[$primaryLocale]) ? [$primaryLocale => $mdata[$primaryLocale]] : [$fk = array_key_first($mdata) => $mdata[$fk]]); + + $pubLocaleData = [ + 'title' => ['text' => $getOtherTitles($titles)], + 'subtitle' => ['text' => $getOtherTitles($subtitles)], + 'keywords' => ['text' => $getMdata('keywords')], + 'abstract' => ['text' => $getMdata('abstract')], + ]; + + foreach($pubLocaleData as $opt => &$item) { + uksort($item['text'], fn (string $a, string $b) => $a === $primaryLocale ? -1 : ($b === $primaryLocale ? 1 : $a <=> $b)); + + $item['headingLang'] = collect($item['text']) + ->map(fn ($_, string $locale) => in_array($opt, $metadataOpts) ? $locale : $uiLocale) + ->toArray(); + } + + $pubLocaleData['primaryTitle'] = $titles[$primaryLocale]; + $pubLocaleData['primarySubtitle'] = $subtitles[$primaryLocale] ?? null; + $pubLocaleData['primaryLocale'] = $primaryLocale; + + return $pubLocaleData; + } } diff --git a/plugins/themes/default/DefaultThemePlugin.php b/plugins/themes/default/DefaultThemePlugin.php index 6ebcb1f4424..01007651f6a 100644 --- a/plugins/themes/default/DefaultThemePlugin.php +++ b/plugins/themes/default/DefaultThemePlugin.php @@ -124,6 +124,26 @@ public function init() 'default' => 'none', ]); + $this->addOption('showMetadata', 'FieldOptions', [ + 'label' => __('plugins.themes.default.option.metadata.label'), + 'description' => __('plugins.themes.default.option.metadata.description'), + 'options' => [ + [ + 'value' => 'titles', + 'label' => __('plugins.themes.default.option.metadata.titleAndSubtitle'), + ], + [ + 'value' => 'keywords', + 'label' => __('article.subject'), + ], + [ + 'value' => 'abstract', + 'label' => __('article.abstract'), + ], + ], + 'default' => [], + ]); + // Load primary stylesheet $this->addStyle('stylesheet', 'styles/index.less'); diff --git a/plugins/themes/default/locale/en/locale.po b/plugins/themes/default/locale/en/locale.po index c783e630ff5..b301d78d629 100644 --- a/plugins/themes/default/locale/en/locale.po +++ b/plugins/themes/default/locale/en/locale.po @@ -98,3 +98,15 @@ msgstr "Next slide" msgid "plugins.themes.default.prevSlide" msgstr "Previous slide" + +msgid "plugins.themes.default.option.metadata.label" +msgstr "Show article metadata on the article landing page" + +msgid "plugins.themes.default.option.metadata.description" +msgstr "Select the article metadata to show in other languages." + +msgid "plugins.themes.default.option.metadata.titleAndSubtitle" +msgstr "Title and subtitle" + +msgid "plugins.themes.default.titleSubtitleSeparator" +msgstr " — " diff --git a/plugins/themes/default/styles/objects/article_details.less b/plugins/themes/default/styles/objects/article_details.less index d8c032b0a0d..6bd98f0b40f 100644 --- a/plugins/themes/default/styles/objects/article_details.less +++ b/plugins/themes/default/styles/objects/article_details.less @@ -66,6 +66,23 @@ display: inline; font-size: @font-base; } + + &.keywords > section { + margin-bottom: @base; + + &:last-of-type { + margin-bottom: 0; + } + } + + &.abstracts .abstract, + &.page_locale_titles { + margin-bottom: @quadruple; + + &:last-of-type { + margin-bottom: 0; + } + } } .sub_item { diff --git a/templates/frontend/objects/article_details.tpl b/templates/frontend/objects/article_details.tpl index 4b6b1463e1b..81692e25797 100755 --- a/templates/frontend/objects/article_details.tpl +++ b/templates/frontend/objects/article_details.tpl @@ -64,6 +64,7 @@ * @uses $licenseUrl string URL to license. Only assigned if license should be * included with published submissions. * @uses $ccLicenseBadge string An image and text with details about the license + * @uses $pubLocaleData array Array of formatted publication locale metadata: titles, abstracts, keywords, * * @hook Templates::Article::Main [] * @hook Templates::Article::Details::Reference [] @@ -91,13 +92,12 @@ {/if} -

- {$publication->getLocalizedTitle(null, 'html')|strip_unsafe_html} +

+ {$pubLocaleData.primaryTitle|strip_unsafe_html}

- - {if $publication->getLocalizedData('subtitle')} -

- {$publication->getLocalizedSubTitle(null, 'html')|strip_unsafe_html} + {if $pubLocaleData.primarySubtitle} +

+ {$pubLocaleData.primarySubtitle|strip_unsafe_html}

{/if} @@ -160,27 +160,51 @@ {/if} + {* Titles in other languages *} + {if !empty(count($pubLocaleData.title.text))} +
+

+ {translate key="article.titlesLanguages" locale=$pubLocaleData.primaryLocale} +

+ {foreach from=$pubLocaleData.title.text key=locale item=title} +

+ {$title|strip_tags} + {if isset($pubLocaleData.subtitle.text[$locale])}{translate key="plugins.themes.default.titleSubtitleSeparator"}{$pubLocaleData.subtitle.text[$locale]|strip_tags}{/if} +

+ {/foreach} +
+ {/if} {* Keywords *} - {if !empty($publication->getLocalizedData('keywords'))} -
-

- {capture assign=translatedKeywords}{translate key="article.subject"}{/capture} - {translate key="semicolon" label=$translatedKeywords} -

- - {foreach name="keywords" from=$publication->getLocalizedData('keywords') item="keyword"} - {$keyword|escape}{if !$smarty.foreach.keywords.last}{translate key="common.commaListSeparator"}{/if} - {/foreach} - -
+ {if !empty(count($pubLocaleData.keywords.text))} +
+ {foreach from=$pubLocaleData.keywords.text key=locale item=keywords} +
+

+ {capture assign=translatedKeywords}{translate key="article.subject" locale=$pubLocaleData.keywords.headingLang[$locale]}{/capture} + {translate key="semicolon" label=$translatedKeywords locale=$pubLocaleData.keywords.headingLang[$locale]} +

+ + {foreach name="keywords" from=$keywords item="keyword"} + {$keyword|escape}{if !$smarty.foreach.keywords.last}{translate key="common.commaListSeparator" locale=$pubLocaleData.keywords.headingLang[$locale]}{/if} + {/foreach} + +
+ {/foreach} +
{/if} {* Abstract *} - {if $publication->getLocalizedData('abstract')} -
-

{translate key="article.abstract"}

- {$publication->getLocalizedData('abstract')|strip_unsafe_html} + {if !empty(count($pubLocaleData.abstract.text))} +
+ {foreach from=$pubLocaleData.abstract.text key=locale item=abstract} +
+

+ {translate key="article.abstract" locale=$pubLocaleData.abstract.headingLang[$locale]} +

+

{$abstract|strip_tags}

+
+ {/foreach}
{/if}