Commit 081e2c5e authored by Charles Bourdot's avatar Charles Bourdot
Browse files

mise en forme et préparation validation

parent 242df865
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -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.**



+0 −3
Original line number Diff line number Diff line
@@ -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

+123 −112
Original line number Diff line number Diff line
<?php

/**
 * Created by PhpStorm.
 * User: dje
@@ -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
@@ -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']);

        }

@@ -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)
@@ -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);
@@ -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);
        }
@@ -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)
@@ -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)
        );
    }
@@ -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)
@@ -317,7 +323,7 @@ public function landingPageHandler($hookName, $params): bool
    }


    function getPluginAssetsPath($request)
    public function getPluginAssetsPath($request)
    {
        return $request->getBaseUrl() . '/' . $this->getPluginPath() . '/resources/';
    }
@@ -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
@@ -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 {
@@ -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
@@ -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);
            }

@@ -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'
@@ -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);
@@ -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'])) {
@@ -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'] . '/';
@@ -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;
        }

    }
@@ -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];
@@ -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);
@@ -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
                );

            }
        }
+27 −20
Original line number Diff line number Diff line
@@ -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));
@@ -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
        );
@@ -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'
+119 −117
Original line number Diff line number Diff line
@@ -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;
@@ -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;

@@ -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
@@ -831,4 +834,3 @@ function initCitationCopy() {
        });
    });
}
 No newline at end of file
 
 No newline at end of file
Loading