From 0eb23c5a19fc43502e260cc7ee70395957d9ae9b Mon Sep 17 00:00:00 2001
From: Alec Smecher <alec@smecher.bc.ca>
Date: Tue, 8 Dec 2020 12:20:17 -0800
Subject: [PATCH] Expose production ready files via JATS

---
 JatsTemplateDownloadHandler.inc.php | 93 +++++++++++++++++++++++++++++
 JatsTemplatePlugin.inc.php          | 58 +++++++++++++++---
 2 files changed, 144 insertions(+), 7 deletions(-)
 create mode 100644 JatsTemplateDownloadHandler.inc.php

diff --git a/JatsTemplateDownloadHandler.inc.php b/JatsTemplateDownloadHandler.inc.php
new file mode 100644
index 0000000..c0af7c4
--- /dev/null
+++ b/JatsTemplateDownloadHandler.inc.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * @file JatsTemplateDownloadHandler.inc.php
+ *
+ * Copyright (c) 2014-2020 Simon Fraser University
+ * Copyright (c) 2003-2020 John Willinsky
+ * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
+ *
+ * @package plugins.generic.jatsTemplate
+ * @class JatsTemplateDownloadHandler
+ */
+
+import('classes.handler.Handler');
+
+use \Firebase\JWT\JWT;
+
+class JatsTemplateDownloadHandler extends Handler {
+	/** @var JatsTemplatePlugin The JATS Template plugin */
+	static $plugin;
+
+	/**
+	 * Provide the JATS template plugin to the handler.
+	 * @param $plugin JATSTemplatePlugin
+	 */
+	static function setPlugin($plugin) {
+		self::$plugin = $plugin;
+	}
+
+	/**
+	 * @copydoc PKPHandler::authorize()
+	 */
+	function authorize($request, &$args, $roleAssignments) {
+		// Permit the use of the Authorization header and an API key for access to unpublished/subscription content
+		if ($header = array_search('Authorization', array_flip(getallheaders()))) {
+			list($bearer, $jwt) = explode(' ', $header);
+			if (strcasecmp($bearer, 'Bearer') == 0) {
+				$apiToken = JWT::decode($jwt, Config::getVar('security', 'api_key_secret', ''), array('HS256'));
+				$this->setApiToken($apiToken);
+			}
+		}
+
+		import('lib.pkp.classes.security.authorization.ContextRequiredPolicy');
+		$this->addPolicy(new ContextRequiredPolicy($request));
+
+		import('classes.security.authorization.OjsJournalMustPublishPolicy');
+		$this->addPolicy(new OjsJournalMustPublishPolicy($request));
+
+		return parent::authorize($request, $args, $roleAssignments);
+	}
+
+	protected function _isUserAllowedAccess($request) {
+		$user = $request->getUser();
+		$context = $request->getContext();
+		if (!$user || !$context) return false;
+		$roleDao = DAORegistry::getDAO('RoleDAO'); /** @var $roleDao RoleDAO */
+		$roles = $roleDao->getByUserId($user->getId(), $context->getId());
+		$allowedAccess = false;
+		foreach ($roles as $role) {
+			if (in_array($role->getRoleId(), [ROLE_ID_MANAGER, ROLE_ID_SUBSCRIPTION_MANAGER])) return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Handle a download request
+	 * @param $args array Arguments array.
+	 * @param $request PKPRequest Request object.
+	 */
+	function download($args, $request) {
+		if (!$this->_isUserAllowedAccess($request)) $request->getDispatcher()->handle404();
+
+		// Check the stage (this is only for consistency with other download URLs in the system
+		// in case the built-in download handler can be used in place of this in the future)
+		$submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO');
+		if ($request->getUserVar('stageId') != WORKFLOW_STAGE_ID_PRODUCTION) $request->getDispatcher()->handle404();
+
+		$submissionId = $request->getUserVar('submissionId');
+		$layoutFiles = Services::get('submissionFile')->getMany([
+			'submissionIds' => [$submissionId],
+			'fileStages' => [SUBMISSION_FILE_PRODUCTION_READY],
+                ]);
+		foreach ($layoutFiles as $layoutFile) {
+			if ($layoutFile->getId() != $request->getUserVar('submissionFileId') || $layoutFile->getData('fileId') != $request->getUserVar('fileId')) continue;
+
+			$filename = Services::get('file')->formatFilename($layoutFile->getData('path'), $layoutFile->getLocalizedData('name'));
+			Services::get('file')->download($layoutFile->getData('fileId'), $filename);
+			return;
+		}
+		$request->getDispatcher()->handle404();
+	}
+}
+
diff --git a/JatsTemplatePlugin.inc.php b/JatsTemplatePlugin.inc.php
index 48321ee..45c5e80 100644
--- a/JatsTemplatePlugin.inc.php
+++ b/JatsTemplatePlugin.inc.php
@@ -21,7 +21,8 @@ public function register($category, $path, $mainContextId = null) {
 		$this->addLocaleData();
 
 		if ($success && $this->getEnabled()) {
-			HookRegistry::register('OAIMetadataFormat_JATS::findJats', array($this, 'callback'));
+			HookRegistry::register('OAIMetadataFormat_JATS::findJats', [$this, 'callbackFindJats']);
+			HookRegistry::register('LoadHandler', [$this, 'callbackHandleContent']);
 		}
 		return $success;
 	}
@@ -41,11 +42,11 @@ public function getDescription() {
 	}
 
 	/**
-	 * Send submission files to iThenticate.
+	 * Prepare JATS template document
 	 * @param $hookName string
 	 * @param $args array
 	 */
-	public function callback($hookName, $args) {
+	public function callbackFindJats($hookName, $args) {
 		$plugin =& $args[0];
 		$record =& $args[1];
 		$candidateFiles =& $args[2];
@@ -212,10 +213,31 @@ function toXml(&$record, $format = null) {
 			$response .= "\t\t\t</kwd-group>\n";
 		}
 
-		$response .=
-			(isset($pageCount)?"\t\t\t<counts><page-count count=\"" . (int) $pageCount. "\" /></counts>\n":'') .
-			"\t\t</article-meta>\n" .
-			"\t</front>\n";
+		$response .= (isset($pageCount)?"\t\t\t<counts><page-count count=\"" . (int) $pageCount. "\" /></counts>\n":'');
+
+		$candidateFound = false;
+		$layoutResponse = "\t\t\t\t<custom-meta-group>";
+		$submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO');
+		$layoutFiles = Services::get('submissionFile')->getMany([
+                        'submissionIds' => [$article->getId()],
+                        'fileStages' => [SUBMISSION_FILE_PRODUCTION_READY],
+                ]);
+		foreach ($layoutFiles as $layoutFile) {
+			$candidateFound = true;
+			$sourceFileUrl = $request->url(null, 'jatsTemplate', 'download', null,
+				[
+					'submissionFileId' => $layoutFile->getId(),
+					'fileId' => $layoutFile->getData('fileId'),
+					'submissionId' => $article->getId(),
+					'stageId' => WORKFLOW_STAGE_ID_PRODUCTION,
+				]
+			);
+			$layoutResponse .= "\t\t\t\t\t<custom-meta>\t\t\t\t\t\t<meta-name>production-ready-file-url</meta-name>\n\t\t\t\t\t\t<meta-value><ext-link ext-link-type=\"uri\" xlink:href=\"" . htmlspecialchars($sourceFileUrl) . "\"/></meta-value>\n\t\t\t\t\t</custom-meta>\n";
+		}
+		$layoutResponse .= "\t\t\t\t</custom-meta-group>";
+		if ($candidateFound) $response .= $layoutResponse;
+
+		$response .= "\t\t</article-meta>\n\t</front>\n";
 
 		// Include body text (for search indexing only)
 		import('classes.search.ArticleSearchIndex');
@@ -278,4 +300,26 @@ function toXml(&$record, $format = null) {
 		$response .= "</article>";
 		return $response;
 	}
+
+	/**
+	 * Declare the handler function to process the actual page PATH
+	 * @param $hookName string The name of the invoked hook
+	 * @param $args array Hook parameters
+	 * @return boolean Hook handling status
+	 */
+	function callbackHandleContent($hookName, $args) {
+		$request = Application::get()->getRequest();
+		$templateMgr = TemplateManager::getManager($request);
+
+		$page =& $args[0];
+		$op =& $args[1];
+
+		if ($page == 'jatsTemplate' && $op == 'download') {
+			define('HANDLER_CLASS', 'JatsTemplateDownloadHandler');
+			$this->import('JatsTemplateDownloadHandler');
+			JatsTemplateDownloadHandler::setPlugin($this);
+			return true;
+		}
+		return false;
+	}
 }