Loading README-fr.md +0 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ Plugin OJS/OMP de chargement de flux XML développé dans le cadre du projet [FN **Ce plugin est en cours de développement, son usage n'est pas destiné à être utilisé en production.** Loading README.md +0 −3 Original line number Diff line number Diff line Loading @@ -22,9 +22,6 @@ Plugin OJS and OMP for XML loading and conversion to HTML, developed as part of **This plugin is currently under development and is not intended for production use.** ## Installation Loading XML2HTMLPlugin.php +123 −112 Original line number Diff line number Diff line <?php /** * Created by PhpStorm. * User: dje Loading @@ -11,9 +12,9 @@ import('lib.pkp.classes.file.PrivateFileManager'); class XML2HTMLPlugin extends GenericPlugin { static $META_XSL_SUFFIX = "-metas2html.xsl"; public static $META_XSL_SUFFIX = '-metas2html.xsl'; public function register($category, $path, $mainContextId = NULL) public function register($category, $path, $mainContextId = null) { // Register the plugin even when it is not enabled Loading @@ -25,15 +26,15 @@ class XML2HTMLPlugin extends GenericPlugin } if ($success && $this->getEnabled()) { HookRegistry::register('ArticleHandler::view::galley', array($this, 'articleHandler')); HookRegistry::register('ArticleHandler::view::galley', [$this, 'articleHandler']); if ($landingEnabled) { HookRegistry::register('ArticleHandler::view', array($this, 'landingPageHandler')); HookRegistry::register('ArticleHandler::view', [$this, 'landingPageHandler']); } //HookRegistry::register('ArticleHandler::download', array($this, 'articleDownloadCallback'), HOOK_SEQUENCE_LATE); HookRegistry::register('LoadHandler', array($this, 'loadImageHandler')); HookRegistry::register('CatalogBookHandler::view', array($this, 'catalogHandler')); HookRegistry::register('LoadHandler', [$this, 'loadImageHandler']); HookRegistry::register('CatalogBookHandler::view', [$this, 'catalogHandler']); } Loading Loading @@ -128,13 +129,14 @@ public function landingPageHandler($hookName, $params): bool $submission = &$params[1]; $galley = & $params[2]; $submissionFile = & $params[3]; $zipMode = $submissionFile->getData('mimetype') === "application/zip"; $zipMode = $submissionFile->getData('mimetype') === 'application/zip'; if ($zipMode) { $this->archiveView($submissionFile->getData('path'), $submission->getId(), $galley->getId(), 'catalog'); return true; } else } else { return false; } } private function archiveView(string $filePath, int $submissionId, int $galleyId, string $op) Loading @@ -153,16 +155,19 @@ public function landingPageHandler($hookName, $params): bool 'format' ); if ($format == NULL || $format == '') $format = 'JATS';//default value is JATS if ($format == null || $format == '') { $format = 'JATS'; }//default value is JATS $xmlFilePath = XML2HTMLPlugin::unzipXMLArchive($filePath); $html = $this->resolveZipImagePaths( XML2HTMLPlugin::transform($xmlFilePath, $format), $submissionId, $galleyId, $xmlFilePath, $this->getRequest(), $op); $xmlFilePath, $this->getRequest(), $op ); //metas transfo $dcMetas = XML2HTMLPlugin::transformMetas($xmlFilePath, $format); Loading Loading @@ -193,16 +198,19 @@ public function landingPageHandler($hookName, $params): bool if (!$html) { $format = $this->getSetting($context->getId(), 'format'); if ($format == NULL || $format == '') $format = 'JATS';//default value is JATS if ($format == null || $format == '') { $format = 'JATS'; }//default value is JATS $xmlFilePath = XML2HTMLPlugin::unzipXMLArchive($filePath); $html = $this->resolveZipImagePaths( XML2HTMLPlugin::transform($xmlFilePath, $format), $submissionId, $galleyId, $xmlFilePath, $this->getRequest(), $op); $xmlFilePath, $this->getRequest(), $op ); //store transfo result file_put_contents($htmlFile, $html, LOCK_EX); } Loading @@ -220,7 +228,7 @@ public function landingPageHandler($hookName, $params): bool /** * get HTML file path from its zip one * exemple : a/b/c/1234.zip -> ojs/files/dir/a/b/c/1234/1234.html * @param $zipPath * * @return string */ private static function getHTMLPathFromZipPath($zipPath) Loading Loading @@ -258,26 +266,23 @@ public function landingPageHandler($hookName, $params): bool } /** * @param $request * @param $verb * @return mixed */ public function getActions($request, $verb) { $router = $request->getRouter(); import('lib.pkp.classes.linkAction.request.AjaxModal'); return array_merge( $this->getEnabled() ? array( $this->getEnabled() ? [ new LinkAction( 'settings', new AjaxModal( $router->url($request, null, null, 'manage', null, array('verb' => 'settings', 'plugin' => $this->getName(), 'category' => 'generic')), $router->url($request, null, null, 'manage', null, ['verb' => 'settings', 'plugin' => $this->getName(), 'category' => 'generic']), $this->getDisplayName() ), __('manager.plugins.settings'), null ), ) : array(), ] : [], parent::getActions($request, $verb) ); } Loading @@ -288,7 +293,8 @@ public function landingPageHandler($hookName, $params): bool * save the form when the user saves it. * * @param array $args * @param Request $request * @param PKPRequest $request * * @return JSONMessage */ public function manage($args, $request) Loading Loading @@ -317,7 +323,7 @@ public function landingPageHandler($hookName, $params): bool } function getPluginAssetsPath($request) public function getPluginAssetsPath($request) { return $request->getBaseUrl() . '/' . $this->getPluginPath() . '/resources/'; } Loading @@ -325,28 +331,30 @@ public function landingPageHandler($hookName, $params): bool /** * Returns the public URL to an uploaded custom CSS file, or null if none is set. * * @param Request $request * @param PKPRequest $request * @param string $settingKey 'cssLandingPageFile' or 'cssSideViewFile' * @return string|null */ function getCssFileUrl($request, string $settingKey): ?string public function getCssFileUrl($request, string $settingKey): ?string { $context = $request->getContext(); if (!$context) return null; if (!$context) { return null; } $filename = $this->getSetting($context->getId(), $settingKey); if (!$filename) return null; if (!$filename) { return null; } return $request->getBaseUrl() . '/' . $this->getPluginPath() . '/resources/custom/' . $filename; } static function transform( public static function transform( string $xmlFilePath, string $format ) { ) { $xsltDir = dirname(__FILE__) . DIRECTORY_SEPARATOR Loading @@ -355,12 +363,13 @@ public function landingPageHandler($hookName, $params): bool . strtolower($format) . DIRECTORY_SEPARATOR; $result = NULL; foreach (glob($xsltDir . "*.xsl") as $xsltPath) { if(basename($xsltPath) == strtolower($format) . XML2HTMLPlugin::$META_XSL_SUFFIX) continue;//do not apply metas xsl $xml = new DOMDocument; if ($result == NULL) { $result = null; foreach (glob($xsltDir . '*.xsl') as $xsltPath) { if (basename($xsltPath) == strtolower($format) . XML2HTMLPlugin::$META_XSL_SUFFIX) { continue; }//do not apply metas xsl $xml = new DOMDocument(); if ($result == null) { // Load the XML source $xml->load($xmlFilePath); } else { Loading @@ -368,11 +377,11 @@ public function landingPageHandler($hookName, $params): bool $xml->loadXML($result); } $xsl = new DOMDocument; $xsl = new DOMDocument(); $xsl->load($xsltPath, LIBXML_DTDATTR); // Configure the transformer $proc = new XSLTProcessor; $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); // attach the xsl rules //lang param Loading @@ -382,7 +391,7 @@ public function landingPageHandler($hookName, $params): bool try { $result = $proc->transformToXML($xml); } catch (Exception $e) { $result = NULL; $result = null; var_dump($e); } Loading @@ -392,11 +401,10 @@ public function landingPageHandler($hookName, $params): bool } static function transformMetas( public static function transformMetas( string $xmlFilePath, string $format ) { ) { $xslt = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'xsl' Loading @@ -405,22 +413,22 @@ public function landingPageHandler($hookName, $params): bool . DIRECTORY_SEPARATOR . strtolower($format) . XML2HTMLPlugin::$META_XSL_SUFFIX; $xml = new DOMDocument; $xml = new DOMDocument(); $xml->load($xmlFilePath); $xsl = new DOMDocument; $xsl = new DOMDocument(); $xsl->load($xslt, LIBXML_DTDATTR); $proc = new XSLTProcessor; $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); return $proc->transformToXML($xml); } /** * @param string $path * @return string|void|null * @throws Exception * * @return string|void|null */ static function unzipXMLArchive(string $path) public static function unzipXMLArchive(string $path) { $fileManager = new FileManager(); $submissionFileExtension = $fileManager->parseFileExtension($path); Loading @@ -428,7 +436,7 @@ public function landingPageHandler($hookName, $params): bool if ($submissionFileExtension == 'zip') { $fileMgr = new PrivateFileManager(); $filesDir = $fileMgr->getBasePath() . DIRECTORY_SEPARATOR; $zip = new ZipArchive; $zip = new ZipArchive(); $zipPath = $filesDir . $path; $zipPathParts = pathinfo($zipPath); if (file_exists($zipPathParts['dirname'] . DIRECTORY_SEPARATOR . $zipPathParts['filename'])) { Loading @@ -438,7 +446,7 @@ public function landingPageHandler($hookName, $params): bool . DIRECTORY_SEPARATOR . $fileInfo['filename'] . '.xml'; } if ($zip->open($zipPath) === TRUE) { if ($zip->open($zipPath) === true) { $nbFiles = $zip->numFiles; if ($nbFiles > 0 && !str_ends_with($zip->getNameIndex(0), '/')) { $basePath = $zipPathParts['dirname'] . DIRECTORY_SEPARATOR . $zipPathParts['filename'] . '/'; Loading @@ -456,10 +464,10 @@ public function landingPageHandler($hookName, $params): bool } } } else { throw new \Exception("Bad zip Submission file."); throw new \Exception('Bad zip Submission file.'); } } else { return NULL; return null; } } Loading @@ -467,9 +475,10 @@ public function landingPageHandler($hookName, $params): bool /** * @param $hookName string * @param $args array * * @brief Handle associated files of the full-text, only images are supported */ function loadImageHandler($hookName, array $args) public function loadImageHandler($hookName, array $args) { $page = $args[0]; $op = $args[1]; Loading @@ -484,14 +493,14 @@ public function landingPageHandler($hookName, $params): bool } static function resolveZipImagePaths( public static function resolveZipImagePaths( string $htmlString, int $submissionId, int $galleyId, string $xmlFilePath, Request $request, string $op = 'article') { PKPRequest $request, string $op = 'article' ) { $pathInfo = pathinfo($xmlFilePath); $extractedZipPath = $pathInfo['dirname']; $files = scandir($extractedZipPath); Loading @@ -499,22 +508,24 @@ public function landingPageHandler($hookName, $params): bool foreach ($files as $file) { $info = pathinfo($file); if ($info["extension"] == 'jpg' || $info["extension"] == 'png') { if ($info['extension'] == 'jpg' || $info['extension'] == 'png') { $request = $request; $filePath = $request->url( null, $op, null, $op, 'zimage', array( [ $submissionId, $galleyId, $file ) ] ); $pattern = preg_quote(rawurlencode($file)); $htmlString = preg_replace( '/([Ss][Rr][Cc]|[Hh][Rr][Ee][Ff]|[Dd][Aa][Tt][Aa])\s*=\s*"([^"]*' . $pattern . ')"/', '\1="' . $filePath . '"', $htmlString); $htmlString ); } } Loading XML2HTMLSettingsForm.inc.php +27 −20 Original line number Diff line number Diff line Loading @@ -4,11 +4,12 @@ import('lib.pkp.classes.form.Form'); import('classes.notification.NotificationManager'); class XML2HTMLSettingsForm extends Form { class XML2HTMLSettingsForm extends Form { private $plugin; function __construct(XML2HTMLPlugin $plugin) { public function __construct(XML2HTMLPlugin $plugin) { $this->plugin = $plugin; parent::__construct($plugin->getTemplateResource('settingsForm.tpl')); $this->addCheck(new FormValidatorPost($this)); Loading Loading @@ -47,14 +48,18 @@ class XML2HTMLSettingsForm extends Form { // URL CSS Landing Page $landingFile = $this->plugin->getSetting($contextId, 'cssLandingPageFile'); $templateMgr->assign('cssLandingPageUrl', $landingFile $templateMgr->assign( 'cssLandingPageUrl', $landingFile ? $request->getBaseUrl() . '/' . $this->plugin->getPluginPath() . '/resources/custom/' . $landingFile : null ); // URL CSS Side View $sideViewFile = $this->plugin->getSetting($contextId, 'cssSideViewFile'); $templateMgr->assign('cssSideViewUrl', $sideViewFile $templateMgr->assign( 'cssSideViewUrl', $sideViewFile ? $request->getBaseUrl() . '/' . $this->plugin->getPluginPath() . '/resources/custom/' . $sideViewFile : null ); Loading Loading @@ -150,7 +155,9 @@ class XML2HTMLSettingsForm extends Form { private function _deleteCssFile(int $contextId, string $settingKey): void { $existing = $this->plugin->getSetting($contextId, $settingKey); if (!$existing) return; if (!$existing) { return; } $path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources' Loading resources/javascript/xml_2_html.js +119 −117 Original line number Diff line number Diff line Loading @@ -554,41 +554,45 @@ function deactivateAllFilters(exceptBtnId) { filters.forEach(({ btnId, injectedId }) => { if (btnId === exceptBtnId) return; const btn = document.getElementById(btnId); if (btn) btn.classList.remove('active'); if (injectedId) { const injected = document.getElementById(injectedId); if (injected) injected.remove(); } }); // Restaurer l'affichage de base // Restaurer l'affichage de l'article (toujours, avant que la fonction // appelante applique ses propres masquages) document.querySelector('.article-wrap').querySelectorAll('*').forEach(el => { el.style.removeProperty('display'); }); } // ------------------------------------------------------- // Filtre Figures // ------------------------------------------------------- function toggleFiguresFilter() { deactivateAllFilters('btn-filter-figures'); const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-figures'); const label = document.getElementById('filter-label'); const isActive = btn.classList.toggle('active'); // Si déjà actif → simple désactivation if (btn.classList.contains('active')) { btn.classList.remove('active'); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); return; } // Désactiver les autres filtres puis activer celui-ci deactivateAllFilters('btn-filter-figures'); btn.classList.add('active'); if (isActive) { // Masquer tout le contenu de l'article articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3').forEach(el => { el.style.display = 'none'; }); articleWrap.querySelectorAll('section').forEach(el => { el.style.display = 'block'; // garder visible pour que les enfants le soient el.style.display = 'block'; }); // S'assurer que les figures et tableaux (fig-table, fig-ill) sont visibles articleWrap.querySelectorAll('figure').forEach(el => { el.style.display = 'block'; let parent = el.parentElement; Loading @@ -597,41 +601,85 @@ function toggleFiguresFilter() { parent = parent.parentElement; } }); } } else { // ------------------------------------------------------- // Filtre Métadonnées // ------------------------------------------------------- function toggleMetadataFilter() { const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-metadata'); articleWrap.querySelectorAll('*').forEach(el => { el.style.removeProperty('display'); // Si déjà actif → simple désactivation if (btn.classList.contains('active')) { btn.classList.remove('active'); const injected = document.getElementById('injected-metadata'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); return; } // Désactiver les autres filtres puis activer celui-ci deactivateAllFilters('btn-filter-metadata'); btn.classList.add('active'); articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3, figure, section').forEach(el => { el.style.display = 'none'; }); const metaContent = document.querySelector('#article-meta'); if (metaContent && !document.getElementById('injected-metadata')) { const metaClone = metaContent.cloneNode(true); metaClone.id = 'injected-metadata'; metaClone.style.cssText = 'display:block; padding: 1em; background: var(--bg, #fff);'; articleWrap.appendChild(metaClone); } else if (document.getElementById('injected-metadata')) { document.getElementById('injected-metadata').style.display = 'block'; } } // ------------------------------------------------------- // Filtre Bibliométrie // ------------------------------------------------------- async function toggleBibliometrics() { deactivateAllFilters('btn-filter-bibliometrics'); const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-bibliometrics'); const isActive = btn.classList.toggle('active'); if (isActive) { // Si déjà actif → simple désactivation if (btn.classList.contains('active')) { btn.classList.remove('active'); const injected = document.getElementById('injected-bibliometrics'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); return; } // Désactiver les autres filtres puis activer celui-ci deactivateAllFilters('btn-filter-bibliometrics'); btn.classList.add('active'); articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3, figure, section').forEach(el => { el.style.display = 'none'; }); // Créer le bloc injecté (ou le ré-afficher si déjà créé) let injected = document.getElementById('injected-bibliometrics'); if (!injected) { if (injected) { injected.style.display = 'block'; return; } injected = document.createElement('div'); injected.id = 'injected-bibliometrics'; injected.style.cssText = 'display:block; padding: 1em;'; injected.innerHTML = '<p>Chargement…</p>'; articleWrap.appendChild(injected); // Données statiques du DOM const published = document.querySelector('.item.published .value')?.innerText.trim() ?? 'N/A'; const section = document.querySelector('.item.issue .sub_item .value')?.innerText.trim() ?? 'N/A'; const keywords = document.querySelector('.item.keywords .value')?.innerText.trim() ?? 'N/A'; const galleys = [...document.querySelectorAll('.galleys_links a')].map(a => a.innerText.trim()).join(', ') || 'N/A'; // Données dynamiques via API const match = window.location.pathname.match(/article\/view\/(\d+)/); const articleId = match ? match[1] : null; Loading @@ -641,69 +689,24 @@ async function toggleBibliometrics() { const res = await fetch(`/index.php/mj/api/v1/stats/publications/${articleId}`); const data = await res.json(); statsHTML = ` <li><strong>Vues plein texte :</strong> ${data.abstractViews ?? 0}</li> <li><strong>Téléchargement XML :</strong> ${data.htmlViews ?? 0}</li> <li><strong>Téléchargement PDF :</strong> ${data.pdfViews ?? 0}</li> <li><strong>Autres téléchargements :</strong> ${data.otherViews ?? 0}</li> <li><strong>Total galley :</strong> ${data.galleyViews ?? 0}</li>`; <li>Vues du résumé : ${data.abstractViews ?? 0}</li> <li>Vues HTML : ${data.htmlViews ?? 0}</li> <li>Vues PDF : ${data.pdfViews ?? 0}</li> <li>Autres téléchargements : ${data.otherViews ?? 0}</li> <li>Total galley : ${data.galleyViews ?? 0}</li>`; } catch (e) {} } injected.innerHTML = ` <h2>Bibliométrie</h2> <h3>Statistiques de consultation</h3> <h4>Statistiques de consultation</h4> <ul>${statsHTML}</ul> <h3>Informations de publication</h3> <h4>Informations de publication</h4> <ul> <li><strong>Publié le :</strong> ${published}</li> <li><strong>Rubrique :</strong> ${section}</li> <li><strong>Mots-clés :</strong> ${keywords}</li> <li><strong>Formats disponibles :</strong> ${galleys}</li> <li>Publié le : ${published}</li> <li>Rubrique : ${section}</li> <li>Mots-clés : ${keywords}</li> <li>Formats disponibles : ${galleys}</li> </ul>`; } else { injected.style.display = 'block'; } } else { const injected = document.getElementById('injected-bibliometrics'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); } } function toggleMetadataFilter() { deactivateAllFilters('btn-filter-metadata'); const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-metadata'); const isActive = btn.classList.toggle('active'); if (isActive) { // Masquer tout le contenu de l'article articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3, figure, section').forEach(el => { el.style.display = 'none'; }); // Récupérer les métadonnées depuis le panneau droit const metaContent = document.querySelector('#article-meta'); if (metaContent && !document.getElementById('injected-metadata')) { const metaClone = metaContent.cloneNode(true); metaClone.id = 'injected-metadata'; metaClone.style.cssText = 'display:block; padding: 1em; background: var(--bg, #fff);'; articleWrap.appendChild(metaClone); } else if (document.getElementById('injected-metadata')) { document.getElementById('injected-metadata').style.display = 'block'; } } else { // Retirer l'injection et restaurer l'affichage const injected = document.getElementById('injected-metadata'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => { el.style.removeProperty('display'); }); } } function openGalleyIframe() { // Récupère le lien galley présent dans la page Loading Loading @@ -831,4 +834,3 @@ function initCitationCopy() { }); }); } No newline at end of file No newline at end of file Loading
README-fr.md +0 −1 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ Plugin OJS/OMP de chargement de flux XML développé dans le cadre du projet [FN **Ce plugin est en cours de développement, son usage n'est pas destiné à être utilisé en production.** Loading
README.md +0 −3 Original line number Diff line number Diff line Loading @@ -22,9 +22,6 @@ Plugin OJS and OMP for XML loading and conversion to HTML, developed as part of **This plugin is currently under development and is not intended for production use.** ## Installation Loading
XML2HTMLPlugin.php +123 −112 Original line number Diff line number Diff line <?php /** * Created by PhpStorm. * User: dje Loading @@ -11,9 +12,9 @@ import('lib.pkp.classes.file.PrivateFileManager'); class XML2HTMLPlugin extends GenericPlugin { static $META_XSL_SUFFIX = "-metas2html.xsl"; public static $META_XSL_SUFFIX = '-metas2html.xsl'; public function register($category, $path, $mainContextId = NULL) public function register($category, $path, $mainContextId = null) { // Register the plugin even when it is not enabled Loading @@ -25,15 +26,15 @@ class XML2HTMLPlugin extends GenericPlugin } if ($success && $this->getEnabled()) { HookRegistry::register('ArticleHandler::view::galley', array($this, 'articleHandler')); HookRegistry::register('ArticleHandler::view::galley', [$this, 'articleHandler']); if ($landingEnabled) { HookRegistry::register('ArticleHandler::view', array($this, 'landingPageHandler')); HookRegistry::register('ArticleHandler::view', [$this, 'landingPageHandler']); } //HookRegistry::register('ArticleHandler::download', array($this, 'articleDownloadCallback'), HOOK_SEQUENCE_LATE); HookRegistry::register('LoadHandler', array($this, 'loadImageHandler')); HookRegistry::register('CatalogBookHandler::view', array($this, 'catalogHandler')); HookRegistry::register('LoadHandler', [$this, 'loadImageHandler']); HookRegistry::register('CatalogBookHandler::view', [$this, 'catalogHandler']); } Loading Loading @@ -128,13 +129,14 @@ public function landingPageHandler($hookName, $params): bool $submission = &$params[1]; $galley = & $params[2]; $submissionFile = & $params[3]; $zipMode = $submissionFile->getData('mimetype') === "application/zip"; $zipMode = $submissionFile->getData('mimetype') === 'application/zip'; if ($zipMode) { $this->archiveView($submissionFile->getData('path'), $submission->getId(), $galley->getId(), 'catalog'); return true; } else } else { return false; } } private function archiveView(string $filePath, int $submissionId, int $galleyId, string $op) Loading @@ -153,16 +155,19 @@ public function landingPageHandler($hookName, $params): bool 'format' ); if ($format == NULL || $format == '') $format = 'JATS';//default value is JATS if ($format == null || $format == '') { $format = 'JATS'; }//default value is JATS $xmlFilePath = XML2HTMLPlugin::unzipXMLArchive($filePath); $html = $this->resolveZipImagePaths( XML2HTMLPlugin::transform($xmlFilePath, $format), $submissionId, $galleyId, $xmlFilePath, $this->getRequest(), $op); $xmlFilePath, $this->getRequest(), $op ); //metas transfo $dcMetas = XML2HTMLPlugin::transformMetas($xmlFilePath, $format); Loading Loading @@ -193,16 +198,19 @@ public function landingPageHandler($hookName, $params): bool if (!$html) { $format = $this->getSetting($context->getId(), 'format'); if ($format == NULL || $format == '') $format = 'JATS';//default value is JATS if ($format == null || $format == '') { $format = 'JATS'; }//default value is JATS $xmlFilePath = XML2HTMLPlugin::unzipXMLArchive($filePath); $html = $this->resolveZipImagePaths( XML2HTMLPlugin::transform($xmlFilePath, $format), $submissionId, $galleyId, $xmlFilePath, $this->getRequest(), $op); $xmlFilePath, $this->getRequest(), $op ); //store transfo result file_put_contents($htmlFile, $html, LOCK_EX); } Loading @@ -220,7 +228,7 @@ public function landingPageHandler($hookName, $params): bool /** * get HTML file path from its zip one * exemple : a/b/c/1234.zip -> ojs/files/dir/a/b/c/1234/1234.html * @param $zipPath * * @return string */ private static function getHTMLPathFromZipPath($zipPath) Loading Loading @@ -258,26 +266,23 @@ public function landingPageHandler($hookName, $params): bool } /** * @param $request * @param $verb * @return mixed */ public function getActions($request, $verb) { $router = $request->getRouter(); import('lib.pkp.classes.linkAction.request.AjaxModal'); return array_merge( $this->getEnabled() ? array( $this->getEnabled() ? [ new LinkAction( 'settings', new AjaxModal( $router->url($request, null, null, 'manage', null, array('verb' => 'settings', 'plugin' => $this->getName(), 'category' => 'generic')), $router->url($request, null, null, 'manage', null, ['verb' => 'settings', 'plugin' => $this->getName(), 'category' => 'generic']), $this->getDisplayName() ), __('manager.plugins.settings'), null ), ) : array(), ] : [], parent::getActions($request, $verb) ); } Loading @@ -288,7 +293,8 @@ public function landingPageHandler($hookName, $params): bool * save the form when the user saves it. * * @param array $args * @param Request $request * @param PKPRequest $request * * @return JSONMessage */ public function manage($args, $request) Loading Loading @@ -317,7 +323,7 @@ public function landingPageHandler($hookName, $params): bool } function getPluginAssetsPath($request) public function getPluginAssetsPath($request) { return $request->getBaseUrl() . '/' . $this->getPluginPath() . '/resources/'; } Loading @@ -325,28 +331,30 @@ public function landingPageHandler($hookName, $params): bool /** * Returns the public URL to an uploaded custom CSS file, or null if none is set. * * @param Request $request * @param PKPRequest $request * @param string $settingKey 'cssLandingPageFile' or 'cssSideViewFile' * @return string|null */ function getCssFileUrl($request, string $settingKey): ?string public function getCssFileUrl($request, string $settingKey): ?string { $context = $request->getContext(); if (!$context) return null; if (!$context) { return null; } $filename = $this->getSetting($context->getId(), $settingKey); if (!$filename) return null; if (!$filename) { return null; } return $request->getBaseUrl() . '/' . $this->getPluginPath() . '/resources/custom/' . $filename; } static function transform( public static function transform( string $xmlFilePath, string $format ) { ) { $xsltDir = dirname(__FILE__) . DIRECTORY_SEPARATOR Loading @@ -355,12 +363,13 @@ public function landingPageHandler($hookName, $params): bool . strtolower($format) . DIRECTORY_SEPARATOR; $result = NULL; foreach (glob($xsltDir . "*.xsl") as $xsltPath) { if(basename($xsltPath) == strtolower($format) . XML2HTMLPlugin::$META_XSL_SUFFIX) continue;//do not apply metas xsl $xml = new DOMDocument; if ($result == NULL) { $result = null; foreach (glob($xsltDir . '*.xsl') as $xsltPath) { if (basename($xsltPath) == strtolower($format) . XML2HTMLPlugin::$META_XSL_SUFFIX) { continue; }//do not apply metas xsl $xml = new DOMDocument(); if ($result == null) { // Load the XML source $xml->load($xmlFilePath); } else { Loading @@ -368,11 +377,11 @@ public function landingPageHandler($hookName, $params): bool $xml->loadXML($result); } $xsl = new DOMDocument; $xsl = new DOMDocument(); $xsl->load($xsltPath, LIBXML_DTDATTR); // Configure the transformer $proc = new XSLTProcessor; $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); // attach the xsl rules //lang param Loading @@ -382,7 +391,7 @@ public function landingPageHandler($hookName, $params): bool try { $result = $proc->transformToXML($xml); } catch (Exception $e) { $result = NULL; $result = null; var_dump($e); } Loading @@ -392,11 +401,10 @@ public function landingPageHandler($hookName, $params): bool } static function transformMetas( public static function transformMetas( string $xmlFilePath, string $format ) { ) { $xslt = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'xsl' Loading @@ -405,22 +413,22 @@ public function landingPageHandler($hookName, $params): bool . DIRECTORY_SEPARATOR . strtolower($format) . XML2HTMLPlugin::$META_XSL_SUFFIX; $xml = new DOMDocument; $xml = new DOMDocument(); $xml->load($xmlFilePath); $xsl = new DOMDocument; $xsl = new DOMDocument(); $xsl->load($xslt, LIBXML_DTDATTR); $proc = new XSLTProcessor; $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); return $proc->transformToXML($xml); } /** * @param string $path * @return string|void|null * @throws Exception * * @return string|void|null */ static function unzipXMLArchive(string $path) public static function unzipXMLArchive(string $path) { $fileManager = new FileManager(); $submissionFileExtension = $fileManager->parseFileExtension($path); Loading @@ -428,7 +436,7 @@ public function landingPageHandler($hookName, $params): bool if ($submissionFileExtension == 'zip') { $fileMgr = new PrivateFileManager(); $filesDir = $fileMgr->getBasePath() . DIRECTORY_SEPARATOR; $zip = new ZipArchive; $zip = new ZipArchive(); $zipPath = $filesDir . $path; $zipPathParts = pathinfo($zipPath); if (file_exists($zipPathParts['dirname'] . DIRECTORY_SEPARATOR . $zipPathParts['filename'])) { Loading @@ -438,7 +446,7 @@ public function landingPageHandler($hookName, $params): bool . DIRECTORY_SEPARATOR . $fileInfo['filename'] . '.xml'; } if ($zip->open($zipPath) === TRUE) { if ($zip->open($zipPath) === true) { $nbFiles = $zip->numFiles; if ($nbFiles > 0 && !str_ends_with($zip->getNameIndex(0), '/')) { $basePath = $zipPathParts['dirname'] . DIRECTORY_SEPARATOR . $zipPathParts['filename'] . '/'; Loading @@ -456,10 +464,10 @@ public function landingPageHandler($hookName, $params): bool } } } else { throw new \Exception("Bad zip Submission file."); throw new \Exception('Bad zip Submission file.'); } } else { return NULL; return null; } } Loading @@ -467,9 +475,10 @@ public function landingPageHandler($hookName, $params): bool /** * @param $hookName string * @param $args array * * @brief Handle associated files of the full-text, only images are supported */ function loadImageHandler($hookName, array $args) public function loadImageHandler($hookName, array $args) { $page = $args[0]; $op = $args[1]; Loading @@ -484,14 +493,14 @@ public function landingPageHandler($hookName, $params): bool } static function resolveZipImagePaths( public static function resolveZipImagePaths( string $htmlString, int $submissionId, int $galleyId, string $xmlFilePath, Request $request, string $op = 'article') { PKPRequest $request, string $op = 'article' ) { $pathInfo = pathinfo($xmlFilePath); $extractedZipPath = $pathInfo['dirname']; $files = scandir($extractedZipPath); Loading @@ -499,22 +508,24 @@ public function landingPageHandler($hookName, $params): bool foreach ($files as $file) { $info = pathinfo($file); if ($info["extension"] == 'jpg' || $info["extension"] == 'png') { if ($info['extension'] == 'jpg' || $info['extension'] == 'png') { $request = $request; $filePath = $request->url( null, $op, null, $op, 'zimage', array( [ $submissionId, $galleyId, $file ) ] ); $pattern = preg_quote(rawurlencode($file)); $htmlString = preg_replace( '/([Ss][Rr][Cc]|[Hh][Rr][Ee][Ff]|[Dd][Aa][Tt][Aa])\s*=\s*"([^"]*' . $pattern . ')"/', '\1="' . $filePath . '"', $htmlString); $htmlString ); } } Loading
XML2HTMLSettingsForm.inc.php +27 −20 Original line number Diff line number Diff line Loading @@ -4,11 +4,12 @@ import('lib.pkp.classes.form.Form'); import('classes.notification.NotificationManager'); class XML2HTMLSettingsForm extends Form { class XML2HTMLSettingsForm extends Form { private $plugin; function __construct(XML2HTMLPlugin $plugin) { public function __construct(XML2HTMLPlugin $plugin) { $this->plugin = $plugin; parent::__construct($plugin->getTemplateResource('settingsForm.tpl')); $this->addCheck(new FormValidatorPost($this)); Loading Loading @@ -47,14 +48,18 @@ class XML2HTMLSettingsForm extends Form { // URL CSS Landing Page $landingFile = $this->plugin->getSetting($contextId, 'cssLandingPageFile'); $templateMgr->assign('cssLandingPageUrl', $landingFile $templateMgr->assign( 'cssLandingPageUrl', $landingFile ? $request->getBaseUrl() . '/' . $this->plugin->getPluginPath() . '/resources/custom/' . $landingFile : null ); // URL CSS Side View $sideViewFile = $this->plugin->getSetting($contextId, 'cssSideViewFile'); $templateMgr->assign('cssSideViewUrl', $sideViewFile $templateMgr->assign( 'cssSideViewUrl', $sideViewFile ? $request->getBaseUrl() . '/' . $this->plugin->getPluginPath() . '/resources/custom/' . $sideViewFile : null ); Loading Loading @@ -150,7 +155,9 @@ class XML2HTMLSettingsForm extends Form { private function _deleteCssFile(int $contextId, string $settingKey): void { $existing = $this->plugin->getSetting($contextId, $settingKey); if (!$existing) return; if (!$existing) { return; } $path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'resources' Loading
resources/javascript/xml_2_html.js +119 −117 Original line number Diff line number Diff line Loading @@ -554,41 +554,45 @@ function deactivateAllFilters(exceptBtnId) { filters.forEach(({ btnId, injectedId }) => { if (btnId === exceptBtnId) return; const btn = document.getElementById(btnId); if (btn) btn.classList.remove('active'); if (injectedId) { const injected = document.getElementById(injectedId); if (injected) injected.remove(); } }); // Restaurer l'affichage de base // Restaurer l'affichage de l'article (toujours, avant que la fonction // appelante applique ses propres masquages) document.querySelector('.article-wrap').querySelectorAll('*').forEach(el => { el.style.removeProperty('display'); }); } // ------------------------------------------------------- // Filtre Figures // ------------------------------------------------------- function toggleFiguresFilter() { deactivateAllFilters('btn-filter-figures'); const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-figures'); const label = document.getElementById('filter-label'); const isActive = btn.classList.toggle('active'); // Si déjà actif → simple désactivation if (btn.classList.contains('active')) { btn.classList.remove('active'); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); return; } // Désactiver les autres filtres puis activer celui-ci deactivateAllFilters('btn-filter-figures'); btn.classList.add('active'); if (isActive) { // Masquer tout le contenu de l'article articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3').forEach(el => { el.style.display = 'none'; }); articleWrap.querySelectorAll('section').forEach(el => { el.style.display = 'block'; // garder visible pour que les enfants le soient el.style.display = 'block'; }); // S'assurer que les figures et tableaux (fig-table, fig-ill) sont visibles articleWrap.querySelectorAll('figure').forEach(el => { el.style.display = 'block'; let parent = el.parentElement; Loading @@ -597,41 +601,85 @@ function toggleFiguresFilter() { parent = parent.parentElement; } }); } } else { // ------------------------------------------------------- // Filtre Métadonnées // ------------------------------------------------------- function toggleMetadataFilter() { const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-metadata'); articleWrap.querySelectorAll('*').forEach(el => { el.style.removeProperty('display'); // Si déjà actif → simple désactivation if (btn.classList.contains('active')) { btn.classList.remove('active'); const injected = document.getElementById('injected-metadata'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); return; } // Désactiver les autres filtres puis activer celui-ci deactivateAllFilters('btn-filter-metadata'); btn.classList.add('active'); articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3, figure, section').forEach(el => { el.style.display = 'none'; }); const metaContent = document.querySelector('#article-meta'); if (metaContent && !document.getElementById('injected-metadata')) { const metaClone = metaContent.cloneNode(true); metaClone.id = 'injected-metadata'; metaClone.style.cssText = 'display:block; padding: 1em; background: var(--bg, #fff);'; articleWrap.appendChild(metaClone); } else if (document.getElementById('injected-metadata')) { document.getElementById('injected-metadata').style.display = 'block'; } } // ------------------------------------------------------- // Filtre Bibliométrie // ------------------------------------------------------- async function toggleBibliometrics() { deactivateAllFilters('btn-filter-bibliometrics'); const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-bibliometrics'); const isActive = btn.classList.toggle('active'); if (isActive) { // Si déjà actif → simple désactivation if (btn.classList.contains('active')) { btn.classList.remove('active'); const injected = document.getElementById('injected-bibliometrics'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); return; } // Désactiver les autres filtres puis activer celui-ci deactivateAllFilters('btn-filter-bibliometrics'); btn.classList.add('active'); articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3, figure, section').forEach(el => { el.style.display = 'none'; }); // Créer le bloc injecté (ou le ré-afficher si déjà créé) let injected = document.getElementById('injected-bibliometrics'); if (!injected) { if (injected) { injected.style.display = 'block'; return; } injected = document.createElement('div'); injected.id = 'injected-bibliometrics'; injected.style.cssText = 'display:block; padding: 1em;'; injected.innerHTML = '<p>Chargement…</p>'; articleWrap.appendChild(injected); // Données statiques du DOM const published = document.querySelector('.item.published .value')?.innerText.trim() ?? 'N/A'; const section = document.querySelector('.item.issue .sub_item .value')?.innerText.trim() ?? 'N/A'; const keywords = document.querySelector('.item.keywords .value')?.innerText.trim() ?? 'N/A'; const galleys = [...document.querySelectorAll('.galleys_links a')].map(a => a.innerText.trim()).join(', ') || 'N/A'; // Données dynamiques via API const match = window.location.pathname.match(/article\/view\/(\d+)/); const articleId = match ? match[1] : null; Loading @@ -641,69 +689,24 @@ async function toggleBibliometrics() { const res = await fetch(`/index.php/mj/api/v1/stats/publications/${articleId}`); const data = await res.json(); statsHTML = ` <li><strong>Vues plein texte :</strong> ${data.abstractViews ?? 0}</li> <li><strong>Téléchargement XML :</strong> ${data.htmlViews ?? 0}</li> <li><strong>Téléchargement PDF :</strong> ${data.pdfViews ?? 0}</li> <li><strong>Autres téléchargements :</strong> ${data.otherViews ?? 0}</li> <li><strong>Total galley :</strong> ${data.galleyViews ?? 0}</li>`; <li>Vues du résumé : ${data.abstractViews ?? 0}</li> <li>Vues HTML : ${data.htmlViews ?? 0}</li> <li>Vues PDF : ${data.pdfViews ?? 0}</li> <li>Autres téléchargements : ${data.otherViews ?? 0}</li> <li>Total galley : ${data.galleyViews ?? 0}</li>`; } catch (e) {} } injected.innerHTML = ` <h2>Bibliométrie</h2> <h3>Statistiques de consultation</h3> <h4>Statistiques de consultation</h4> <ul>${statsHTML}</ul> <h3>Informations de publication</h3> <h4>Informations de publication</h4> <ul> <li><strong>Publié le :</strong> ${published}</li> <li><strong>Rubrique :</strong> ${section}</li> <li><strong>Mots-clés :</strong> ${keywords}</li> <li><strong>Formats disponibles :</strong> ${galleys}</li> <li>Publié le : ${published}</li> <li>Rubrique : ${section}</li> <li>Mots-clés : ${keywords}</li> <li>Formats disponibles : ${galleys}</li> </ul>`; } else { injected.style.display = 'block'; } } else { const injected = document.getElementById('injected-bibliometrics'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => el.style.removeProperty('display')); } } function toggleMetadataFilter() { deactivateAllFilters('btn-filter-metadata'); const articleWrap = document.querySelector('.article-wrap'); const btn = document.getElementById('btn-filter-metadata'); const isActive = btn.classList.toggle('active'); if (isActive) { // Masquer tout le contenu de l'article articleWrap.querySelectorAll('p, table.linguistic, ul.index-unordered, h1, h2, h3, figure, section').forEach(el => { el.style.display = 'none'; }); // Récupérer les métadonnées depuis le panneau droit const metaContent = document.querySelector('#article-meta'); if (metaContent && !document.getElementById('injected-metadata')) { const metaClone = metaContent.cloneNode(true); metaClone.id = 'injected-metadata'; metaClone.style.cssText = 'display:block; padding: 1em; background: var(--bg, #fff);'; articleWrap.appendChild(metaClone); } else if (document.getElementById('injected-metadata')) { document.getElementById('injected-metadata').style.display = 'block'; } } else { // Retirer l'injection et restaurer l'affichage const injected = document.getElementById('injected-metadata'); if (injected) injected.remove(); articleWrap.querySelectorAll('*').forEach(el => { el.style.removeProperty('display'); }); } } function openGalleyIframe() { // Récupère le lien galley présent dans la page Loading Loading @@ -831,4 +834,3 @@ function initCitationCopy() { }); }); } No newline at end of file No newline at end of file