From d18de42c0f06590bb529e77951efe964fbc89251 Mon Sep 17 00:00:00 2001 From: edersotto Date: Thu, 2 Jul 2026 13:51:41 -0300 Subject: [PATCH] Add OJS 3.5 support and a configurable article count - Update to OJS 3.5 APIs: replace CacheManager with Illuminate Cache facade, AppLocale/Services with the Locale facade / Repo, and adjust namespaced imports. - New 'Number of articles' (mostReadCount) setting to control how many items the block shows (defaults preserved when unset). - Harden the block template: escape output and only render when there are results; rename the block CSS class to block_most_read. - PSR-12 formatting; bump version to 3.5.0.1. Co-Authored-By: Claude Opus 4.8 --- MostReadBlockPlugin.php | 283 +++++++++++++++++++------------------ MostReadSettingsForm.php | 61 ++++---- locale/en/locale.po | 9 ++ locale/pt_BR/locale.po | 11 +- templates/block.tpl | 16 ++- templates/settingsForm.tpl | 4 + version.xml | 4 +- 7 files changed, 210 insertions(+), 178 deletions(-) diff --git a/MostReadBlockPlugin.php b/MostReadBlockPlugin.php index 015af49..767ebd9 100644 --- a/MostReadBlockPlugin.php +++ b/MostReadBlockPlugin.php @@ -1,13 +1,14 @@ getPluginPath() . '/settings.xml'; - } - - /** - * Get the display name of this plugin. - * @return String - */ - function getDisplayName() { - return __('plugins.blocks.mostRead.displayName'); - } - - /** - * Get a description of the plugin. - */ - function getDescription() { - return __('plugins.blocks.mostRead.description'); - } - - - /** - * @copydoc Plugin::getActions() - */ - function getActions($request, $actionArgs) { - $router = $request->getRouter(); - import('lib.pkp.classes.linkAction.request.AjaxModal'); - return array_merge( - $this->getEnabled()?array( - new LinkAction( - 'settings', - new AjaxModal( - $router->url($request, null, null, 'manage', null, array_merge($actionArgs, array('verb' => 'settings'))), - $this->getDisplayName() - ), - __('manager.plugins.settings'), - null - ), - ):array(), - parent::getActions($request, $actionArgs) - ); - } +class MostReadBlockPlugin extends BlockPlugin +{ + /** Cache lifetime in seconds (1 day). */ + private const CACHE_TTL = 60 * 60 * 24; + + /** + * Install default settings on journal creation. + */ + public function getContextSpecificPluginSettingsFile() + { + return $this->getPluginPath() . '/settings.xml'; + } + + /** + * Get the display name of this plugin. + */ + public function getDisplayName() + { + return __('plugins.blocks.mostRead.displayName'); + } + + /** + * Get a description of the plugin. + */ + public function getDescription() + { + return __('plugins.blocks.mostRead.description'); + } + + /** + * @copydoc Plugin::getActions() + */ + public function getActions($request, $actionArgs) + { + $router = $request->getRouter(); + return array_merge( + $this->getEnabled() ? [ + new LinkAction( + 'settings', + new AjaxModal( + $router->url($request, null, null, 'manage', null, array_merge($actionArgs, ['verb' => 'settings'])), + $this->getDisplayName() + ), + __('manager.plugins.settings'), + null + ), + ] : [], + parent::getActions($request, $actionArgs) + ); + } /** * @copydoc Plugin::manage() @@ -103,95 +106,99 @@ public function manage($args, $request) return parent::manage($args, $request); } - /** - * @copydoc BlockPlugin::getContents - */ - function getContents($templateMgr, $request = null) - { - $context = $request->getContext(); - if (!$context) return ''; - - $cacheManager = CacheManager::getManager(); - $cache = $cacheManager->getCache($context->getId(), 'mostread' , array($this, 'getMostReadCache')); - - $daysToStale = 1; - $cachedMetrics = false; - - if (time() - $cache->getCacheTime() > 60 * 60 * 24 * $daysToStale) { - $cachedMetrics = $cache->getContents(); - $cache->flush(); - } - - $metrics = $cache->getContents(); - - if (!$metrics && $cachedMetrics) { - $metrics = $cachedMetrics; - $cache->setEntireCache($cachedMetrics); - } elseif (!$metrics) { - $cache->flush(); - } - - $mostReadBlockTitleSetting = json_decode( - $this->getSetting($context->getId(), 'mostReadBlockTitle'), - true - ) ?: null; - - $locale = AppLocale::getLocale(); - $mostReadBlockTitle = $mostReadBlockTitleSetting[$locale] ?? null; - $templateMgr->assign('blockTitle', $mostReadBlockTitle); - - $mostRead = []; - foreach($metrics as $metric){ - $submission = Repo::submission()->get($metric['submissionId']); - if(isset($submission) && $submission?->getCurrentPublication()->getData('status') === PKPSubmission::STATUS_PUBLISHED) - { - $mostRead[] = [ - 'url' => Application::get()->getRequest()->url($context?->getPath(), 'article', 'view', [$submission->getBestId()]), - 'metric' => $metric['metric'], - 'title' => $submission?->getCurrentPublication()->getLocalizedFullTitle($locale, 'html') - ]; - } - } - - $templateMgr->assign('mostRead', $mostRead); - return parent::getContents($templateMgr, $request); - } - - /** - * Set cache - * @param $cache object - */ - - function getMostReadCache($cache): array - { - $mostReadDays = (int) $this->getSetting($cache->context, 'mostReadDays'); - if (empty($mostReadDays)){ - $mostReadDays = 7; - } - $dayString = "-" . $mostReadDays . " days"; - - $mostRead = Services::get('publicationStats')->getTotals([ + /** + * @copydoc BlockPlugin::getContents() + */ + public function getContents($templateMgr, $request = null) + { + $context = $request?->getContext(); + if (!$context) { + return ''; + } + + $contextId = $context->getId(); + + $metrics = Cache::remember( + $this->getCacheKey($contextId), + self::CACHE_TTL, + fn () => $this->loadMostRead($contextId) + ); + + $locale = Locale::getLocale(); + + $mostReadBlockTitle = (array) json_decode($this->getSetting($contextId, 'mostReadBlockTitle') ?? ''); + $blockTitle = $mostReadBlockTitle[$locale] ?? ''; + $templateMgr->assign('blockTitle', $blockTitle); + + $mostRead = []; + foreach ($metrics as $metric) { + $submission = Repo::submission()->get($metric['submissionId']); + if (!$submission) { + continue; + } + $publication = $submission->getCurrentPublication(); + if (!$publication || (int) $publication->getData('status') !== PKPSubmission::STATUS_PUBLISHED) { + continue; + } + $mostRead[] = [ + 'url' => $request->url($context->getPath(), 'article', 'view', [$submission->getBestId()]), + 'metric' => $metric['metric'], + 'title' => $publication->getLocalizedFullTitle($locale, 'html'), + ]; + } + + $templateMgr->assign('mostRead', $mostRead); + return parent::getContents($templateMgr, $request); + } + + /** + * Build the cache key for a given context. + */ + public function getCacheKey(int $contextId): string + { + return "plugins.blocks.mostRead.{$contextId}"; + } + + /** + * Clear the cached metrics for a given context. + */ + public function clearCache(int $contextId): void + { + Cache::forget($this->getCacheKey($contextId)); + } + + /** + * Query the most read submissions for a context. + * + * @return array + */ + public function loadMostRead(int $contextId): array + { + $mostReadDays = (int) $this->getSetting($contextId, 'mostReadDays'); + if (empty($mostReadDays)) { + $mostReadDays = 7; + } + $dayString = '-' . $mostReadDays . ' days'; + + $mostReadCount = (int) $this->getSetting($contextId, 'mostReadCount'); + if ($mostReadCount < 1) { + $mostReadCount = 5; + } + + $mostRead = app()->get('publicationStats')->getTotals([ 'dateStart' => date('Y-m-d', strtotime($dayString)), - 'contextIds' => [$cache->context], - 'count' => 5, - 'assocTypes' => [Application::ASSOC_TYPE_SUBMISSION_FILE], + 'contextIds' => [$contextId], + 'count' => $mostReadCount, + 'assocTypes' => [Application::ASSOC_TYPE_SUBMISSION_FILE], ]); - $results = (new Collection($mostRead)) - ->map(function($result) { - $submission = Repo::submission()->get($result->submission_id); - return [ - 'submissionId' => $result->submission_id, - 'metric' => $result->metric - ]; - }) - ->filter(function($result) { - return $result['submissionId'] && $result['metric']; - }) + return (new Collection($mostRead)) + ->map(fn ($result) => [ + 'submissionId' => $result->submission_id, + 'metric' => $result->metric, + ]) + ->filter(fn ($result) => $result['submissionId'] && $result['metric']) + ->values() ->toArray(); - - $cache->setEntireCache($results); - return $results; } } -?> diff --git a/MostReadSettingsForm.php b/MostReadSettingsForm.php index f137c69..ae9908c 100644 --- a/MostReadSettingsForm.php +++ b/MostReadSettingsForm.php @@ -1,31 +1,35 @@ getTemplateResource('settingsForm.tpl')); - $this->addCheck(new \PKP\form\validation\FormValidator($this, 'mostReadDays', 'required', 'plugins.blocks.mostRead.settings.mostReadDaysRequired')); - - $this->addCheck(new \PKP\form\validation\FormValidatorPost($this)); - $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); + $this->addCheck(new FormValidator($this, 'mostReadDays', 'required', 'plugins.blocks.mostRead.settings.mostReadDaysRequired')); + $this->addCheck(new FormValidatorCustom($this, 'mostReadCount', 'optional', 'plugins.blocks.mostRead.settings.mostReadCountInvalid', fn ($value) => ctype_digit((string) $value) && (int) $value >= 1)); + $this->addCheck(new FormValidatorPost($this)); + $this->addCheck(new FormValidatorCSRF($this)); } /** @@ -51,10 +55,11 @@ public function __construct($plugin, $contextId) */ public function initData() { - $mostReadBlockTitle = (array) json_decode($this->_plugin->getSetting($this->_contextId, 'mostReadBlockTitle')); + $mostReadBlockTitle = (array) json_decode($this->_plugin->getSetting($this->_contextId, 'mostReadBlockTitle') ?? ''); $this->_data = [ 'mostReadDays' => $this->_plugin->getSetting($this->_contextId, 'mostReadDays'), - 'mostReadBlockTitle' => $mostReadBlockTitle, + 'mostReadCount' => $this->_plugin->getSetting($this->_contextId, 'mostReadCount'), + 'mostReadBlockTitle' => $mostReadBlockTitle, ]; } @@ -63,8 +68,8 @@ public function initData() */ public function readInputData() { - $this->readUserVars(array('mostReadDays', 'mostReadBlockTitle')); - } + $this->readUserVars(['mostReadDays', 'mostReadCount', 'mostReadBlockTitle']); + } /** * @copydoc Form::fetch() @@ -83,18 +88,14 @@ public function fetch($request, $template = null, $display = false) */ public function execute(...$functionArgs) { - $mostReadBlockTitle = json_encode($this->getData('mostReadBlockTitle')); + $mostReadBlockTitle = json_encode($this->getData('mostReadBlockTitle')); $this->_plugin->updateSetting($this->_contextId, 'mostReadDays', $this->getData('mostReadDays'), 'string'); - $this->_plugin->updateSetting($this->_contextId, 'mostReadBlockTitle', $mostReadBlockTitle, 'string'); - - # empty current cache - $cacheManager = CacheManager::getManager(); - $cache = $cacheManager->getCache('mostread', $this->_contextId, array($this->_plugin, 'getMostReadCache')); - $cache->flush(); - parent::execute(...$functionArgs); + $this->_plugin->updateSetting($this->_contextId, 'mostReadCount', $this->getData('mostReadCount'), 'string'); + $this->_plugin->updateSetting($this->_contextId, 'mostReadBlockTitle', $mostReadBlockTitle, 'string'); + + // Empty the current cache so new settings take effect immediately + $this->_plugin->clearCache($this->_contextId); + + return parent::execute(...$functionArgs); } } - -if (!PKP_STRICT_MODE) { - class_alias('\APP\plugins\blocks\mostRead\MostReadSettingsForm', '\MostReadSettingsForm'); -} \ No newline at end of file diff --git a/locale/en/locale.po b/locale/en/locale.po index 7161eda..cf31a97 100644 --- a/locale/en/locale.po +++ b/locale/en/locale.po @@ -31,3 +31,12 @@ msgstr "Block title" msgid "plugins.blocks.mostRead.settings.saved" msgstr "Most Read Block plugin settings saved" + +msgid "plugins.blocks.mostRead.settings.mostReadDaysRequired" +msgstr "Please enter the number of days to consider." + +msgid "plugins.blocks.mostRead.settings.count" +msgstr "Number of articles" + +msgid "plugins.blocks.mostRead.settings.mostReadCountInvalid" +msgstr "Please enter a whole number greater than or equal to 1." diff --git a/locale/pt_BR/locale.po b/locale/pt_BR/locale.po index b56993d..eb5d7d8 100644 --- a/locale/pt_BR/locale.po +++ b/locale/pt_BR/locale.po @@ -30,4 +30,13 @@ msgid "plugins.blocks.mostRead.settings.blockTitle" msgstr "Título do bloco" msgid "plugins.blocks.mostRead.settings.saved" -msgstr "Configurações do plug-in do bloco de artigos mais lido" \ No newline at end of file +msgstr "Configurações do plug-in do bloco de artigos mais lido" + +msgid "plugins.blocks.mostRead.settings.mostReadDaysRequired" +msgstr "Informe a quantidade de dias a considerar." + +msgid "plugins.blocks.mostRead.settings.count" +msgstr "Quantidade de artigos" + +msgid "plugins.blocks.mostRead.settings.mostReadCountInvalid" +msgstr "Informe um número inteiro maior ou igual a 1." diff --git a/templates/block.tpl b/templates/block.tpl index d51a5bb..6aeeb56 100644 --- a/templates/block.tpl +++ b/templates/block.tpl @@ -1,5 +1,5 @@ {** - * plugins/blocks/mostRead/block.tpl + * plugins/blocks/mostRead/templates/block.tpl * * Copyright (c) 2014-2024 Simon Fraser University * Copyright (c) 2003-2024 John Willinsky @@ -7,16 +7,18 @@ * * "Most Read" block. *} -
+{if !empty($mostRead)} +
- {if isset($blockTitle) }{$blockTitle}{/if} -
    + {if !empty($blockTitle)}{$blockTitle|escape}{/if} + +
+{/if} diff --git a/templates/settingsForm.tpl b/templates/settingsForm.tpl index 14e099a..db9c051 100644 --- a/templates/settingsForm.tpl +++ b/templates/settingsForm.tpl @@ -26,6 +26,10 @@ {fbvElement type="text" label="plugins.blocks.mostRead.settings.days" id="mostReadDays" value=$mostReadDays} {/fbvFormSection} + {fbvFormSection for="mostReadCount"} + {fbvElement type="text" label="plugins.blocks.mostRead.settings.count" id="mostReadCount" value=$mostReadCount} + {/fbvFormSection} + {fbvFormSection for="mostReadBlockTitle"} {fbvElement type="text" label="plugins.blocks.mostRead.settings.blockTitle" id="mostReadBlockTitle" value=$mostReadBlockTitle multilingual=true} {/fbvFormSection} diff --git a/version.xml b/version.xml index ddb15d6..60cf271 100644 --- a/version.xml +++ b/version.xml @@ -13,8 +13,8 @@ mostRead plugins.blocks - 3.4.0.1 - 2024-07-26 + 3.5.0.1 + 2026-07-02 1 MostReadBlockPlugin