Skip to content

Commit

Permalink
pkp#7505 Initial Commit Upload - Delete - Download JATS File
Browse files Browse the repository at this point in the history
  • Loading branch information
defstat committed Nov 24, 2023
1 parent 0ede440 commit ea238b7
Show file tree
Hide file tree
Showing 8 changed files with 602 additions and 51 deletions.
260 changes: 212 additions & 48 deletions api/v1/submissions/PKPSubmissionController.php

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions classes/components/listPanels/JatsListPanel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* @file classes/components/listPanels/JatsListPanel.php
*
* Copyright (c) 2014-2021 Simon Fraser University
* Copyright (c) 2000-2021 John Willinsky
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
*
* @class JatsListPanel
*
* @ingroup classes_components_list
*
* @brief A Panel component for viewing and managing publication's JATS Files
*/

namespace PKP\components\listPanels;

use APP\core\Application;
use APP\facades\Repo;
use APP\publication\Publication;
use APP\submission\Submission;
use PKP\components\forms\publication\ContributorForm;
use PKP\context\Context;
use PKP\submissionFile\SubmissionFile;

class JatsListPanel extends ListPanel
{
public Submission $submission;
public Publication $publication;
public Context $context;
public array $locales;

/** Whether the user can edit the current publication */
public bool $canEditPublication;

public function __construct(
string $id,
string $title,
Submission $submission,
Context $context,
array $locales,
bool $canEditPublication = false,
Publication $publication
) {
parent::__construct($id, $title);
$this->submission = $submission;
$this->context = $context;
$this->locales = $locales;
$this->canEditPublication = $canEditPublication;
$this->publication = $publication;
}

/**
* @copydoc ListPanel::getConfig()
*/
public function getConfig()
{
$config = parent::getConfig();

// Remove some props not used in this list panel
unset($config['description']);
unset($config['expanded']);
unset($config['headingLevel']);

$config = array_merge(
$config,
[
'canEditPublication' => $this->canEditPublication,
'publicationApiUrlFormat' => $this->getPublicationUrlFormat(),
'uploadProgressLabel' => __('submission.upload.percentComplete'),
'fileStage' => SubmissionFile::SUBMISSION_FILE_JATS,
'i18nConfirmDeleteFileTitle' => __('publication.jats.confirmDeleteFileTitle'),
'i18nDeleteFileMessage' => __('publication.jats.confirmDeleteFileMessage'),
'i18nConfirmDeleteFileButton' => __('publication.jats.confirmDeleteFileButton'),
'downloadDefaultJatsFileName' => Repo::submissionFile()->getDefaultJatsFileName($this->submission->getId(), $this->publication->getId()),
]
);

return $config;
}

/**
* Get an example of the url to a publication's API endpoint,
* with a placeholder instead of the publication id, eg:
*
* http://example.org/api/v1/submissions/1/publications/__publicationId__
*/
protected function getPublicationUrlFormat(): string
{
return Application::get()->getRequest()->getDispatcher()->url(
Application::get()->getRequest(),
Application::ROUTE_API,
$this->context->getPath(),
'submissions/' . $this->submission->getId() . '/publications/__publicationId__/jats'
);
}
}
16 changes: 16 additions & 0 deletions classes/db/DAOResultFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,22 @@ public function toAssociativeArray($idField = 'id')
}
return $returner;
}

/**
* Convert this iterator to a collection.
*
* @return Collection<T>
*/
public function toCollection(): Collection
{
$retCollection = collect();

while ($object = $this->next()) {
$retCollection->push($object);
}

return $retCollection;
}
}

if (!PKP_STRICT_MODE) {
Expand Down
234 changes: 233 additions & 1 deletion classes/submissionFile/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,31 @@
use APP\facades\Repo;
use APP\notification\Notification;
use APP\notification\NotificationManager;
use APP\plugins\generic\jatsTemplate\classes\Article;
use APP\plugins\generic\jatsTemplate\classes\ArticleBack;
use APP\plugins\generic\jatsTemplate\classes\ArticleBody;
use APP\plugins\generic\jatsTemplate\classes\ArticleFront;
use APP\publication\Publication;
use APP\submission\Submission;
use DOMDocument;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\LazyCollection;
use PKP\config\Config;
use PKP\core\Core;
use PKP\core\PKPApplication;
use PKP\db\DAORegistry;
use PKP\file\FileManager;
use PKP\file\TemporaryFileManager;
use PKP\log\event\SubmissionFileEventLogEntry;
use PKP\log\SubmissionEmailLogDAO;
use PKP\log\SubmissionEmailLogEntry;
use PKP\mail\mailables\RevisedVersionNotify;
use PKP\note\NoteDAO;
use PKP\notification\PKPNotification;
use PKP\oai\OAIRecord;
use PKP\plugins\Hook;
use PKP\query\QueryDAO;
use PKP\security\authorization\SubmissionFileAccessPolicy;
Expand All @@ -54,6 +66,9 @@ abstract class Repository
/** @var array<int> $reviewFileStages The file stages that are part of a review workflow stage */
public array $reviewFileStages = [];

public ?Submission $submission = null;
public ?Publication $publication = null;

public function __construct(DAO $dao, Request $request, PKPSchemaService $schemaService)
{
$this->schemaService = $schemaService;
Expand Down Expand Up @@ -662,7 +677,8 @@ public function getWorkflowStageId(SubmissionFile $submissionFile): ?int

if (
$fileStage === SubmissionFile::SUBMISSION_FILE_PROOF ||
$fileStage === SubmissionFile::SUBMISSION_FILE_PRODUCTION_READY
$fileStage === SubmissionFile::SUBMISSION_FILE_PRODUCTION_READY ||
$fileStage === SubmissionFile::SUBMISSION_FILE_JATS
) {
return WORKFLOW_STAGE_ID_PRODUCTION;
}
Expand Down Expand Up @@ -843,4 +859,220 @@ protected function getSubmissionFileLogData(SubmissionFile $submissionFile): arr
'username' => $user?->getUsername(),
];
}

/**
* Add jatsContent for Submission files that correspont to
*/
public function getJatsFileProps(SubmissionFile $submissionFile, array $genres): array
{
$fileProps = $this->getSchemaMap()->summarize($submissionFile, $genres);
$jatsFileName = Config::getVar('files', 'files_dir') . '/' . $fileProps['path'] .'';
$xmlString = file_get_contents($jatsFileName);

if ($xmlString) {
$fileProps['jatsContent'] = $xmlString;
}

return $fileProps;
}

/**
* Returns the SubmissionFile, if any, that corresponds to the JATS contents of the given submission/publication
*/
public function getJatsFiles(int $submissionId, int $publicationId): ?SubmissionFile
{
$this->validateSubmissionAndPublication($submissionId, $publicationId);

$submissionFile = Repo::submissionFile()
->getCollector()
->filterBySubmissionIds([$this->submission->getId()])
->filterByFileStages([SubmissionFile::SUBMISSION_FILE_JATS])
->filterByAssoc(Application::ASSOC_TYPE_PUBLICATION, [$this->publication->getId()])
->getMany()
->first();

return $submissionFile;
}

/**
* Returns the name of the file that will contain the default JATS content
*/
public function getDefaultJatsFileName(int $submissionId, int $publicationId): string
{
return 'jats-' . $submissionId . '-' . $publicationId . '-' . (string) mt_rand(100000, 999999) . '.xml';
}

/**
* Creates the default JATS XML Content from the given submission/publication metadata
*/
public function createDefaultContentJatsFile(int $submissionId, int $publicationId): string
{
$submission = Repo::submission()->get($submissionId);
$context = Services::get('context')->get($submission->getData('contextId'));
$publication = Repo::publication()->get($publicationId);
$section = Repo::section()->get($submission->getSectionId());

$issue = null;
if ($publication->getData('issueId')) {
$issue = Repo::issue()->get($publication->getData('issueId'));
}

$exportXml = $this->convertSubmissionToJatsXml($submission, $context, $section, $issue, $publication, Application::get()->getRequest());

return $exportXml;
}

/**
* Base function that will add a new JATS file
*/
public function addJatsFile(
string $fileTmpName,
string $fileName,
int $submissionId,
?int $publicationId = null,
int $type = SubmissionFile::SUBMISSION_FILE_JATS,
array $params = []
): SubmissionFile
{
$this->validateSubmissionAndPublication($submissionId, $publicationId);

$context = $this->request->getContext();
$user = $this->request->getUser();

$existingJatsFile = $this->getJatsFiles($this->submission->getId(), $this->publication->getId());
if ($existingJatsFile) {
throw new Exception('A JATS file already exists');
}

$fileManager = new FileManager();
$extension = $fileManager->parseFileExtension($fileName);

$submissionDir = Repo::submissionFile()
->getSubmissionDir(
$this->submission->getData('contextId'),
$this->submission->getId()
);

$fileId = Services::get('file')->add(
$fileTmpName,
$submissionDir . '/' . uniqid() . '.' . $extension
);

$params['fileId'] = $fileId;
$params['submissionId'] = $this->submission->getId();
$params['uploaderUserId'] = $user->getId();
$params['fileStage'] = $type;

$primaryLocale = $context->getPrimaryLocale();
$allowedLocales = $context->getData('supportedSubmissionLocales');

$params['name'] = null;
$params['name'][$primaryLocale] = $fileName;

// If no genre has been set and there is only one genre possible, set it automatically
/** @var GenreDAO */
$genreDao = DAORegistry::getDAO('GenreDAO');
$genres = $genreDao->getEnabledByContextId($context->getId());

if (empty($params['genreId'])) {

[$firstGenre, $secondGenre] = [$genres->next(), $genres->next()];
if ($firstGenre && !$secondGenre) {
$params['genreId'] = $firstGenre->getId();
}
}

$params['assocType'] = Application::ASSOC_TYPE_PUBLICATION;
$params['assocId'] = $this->publication->getId();

$errors = Repo::submissionFile()
->validate(
null,
$params,
$allowedLocales,
$primaryLocale
);

if (!empty($errors)) {
Services::get('file')->delete($fileId);
throw new Exception(''. implode(', ', $errors));
}

$submissionFile = Repo::submissionFile()
->newDataObject($params);

$submissionFileId = Repo::submissionFile()
->add($submissionFile);

$submissionFile = Repo::submissionFile()
->get($submissionFileId);

return $submissionFile;
}

/**
* Function to get the Context's genres
*/
public function getGenres(int $contextId) : Collection
{
/** @var \PKP\submission\GenreDAO $genreDao */
$genreDao = DAORegistry::getDAO('GenreDAO');
return $genreDao->getByContextId($contextId)
->toCollection();
}

/**
* Given a submissionId and a publicationId this function returns true if the given publicationId
* corresponds to the given submission. If no publicationId given, the current publication will be
* used
*/
protected function validateSubmissionAndPublication(int $submissionId, ?int $publicationId) : bool
{
if (isset($this->submission) && isset($this->publication)) {
return true;
}

$this->submission = Repo::submission()
->get($submissionId);

if (!$this->submission) {
throw new Exception('The given submission can not be found');
}

$this->publication = $this->submission->getCurrentPublication();

if ($publicationId) {
$this->publication = Repo::publication()
->get($publicationId);

if (!$this->publication) {
throw new Exception('The given publication can not be found');
}

if ($this->publication->getData('submissionId') != $submissionId) {
throw new Exception('The given publication is not part of the given submission');
}
}

return true;
}

/**
* Given a submission and a publication this function returns the JATS XML contents provided by the
* submission/publication metadata
*/
protected function convertSubmissionToJatsXml($submission, $journal, $section, $issue, $publication, $request): string
{
$articleJats = new Article();

$articleJats->preserveWhiteSpace = false;
$articleJats->formatOutput = true;

$jatsXML = $articleJats->convertSubmission($submission, $journal, $section, $issue, $publication, $request);

$articleJats->loadXML($jatsXML);
$formattedXml = $articleJats->saveXML();

return $formattedXml;
}
}
Loading

0 comments on commit ea238b7

Please sign in to comment.