first commit
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/DOIPubIdExportPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class DOIPubIdExportPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Basis class for DOI XML metadata export plugins
|
||||
*/
|
||||
|
||||
namespace APP\plugins;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Issue;
|
||||
use APP\journal\Journal;
|
||||
use APP\submission\Submission;
|
||||
use APP\template\TemplateManager;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\galley\Galley;
|
||||
use PKP\submission\PKPSubmission;
|
||||
|
||||
// Configuration errors.
|
||||
define('DOI_EXPORT_CONFIG_ERROR_DOIPREFIX', 0x01);
|
||||
|
||||
// The name of the setting used to save the registered DOI.
|
||||
define('DOI_EXPORT_REGISTERED_DOI', 'registeredDoi');
|
||||
|
||||
abstract class DOIPubIdExportPlugin extends PubObjectsExportPlugin
|
||||
{
|
||||
/**
|
||||
* @copydoc ImportExportPlugin::display()
|
||||
*/
|
||||
public function display($args, $request)
|
||||
{
|
||||
switch (array_shift($args)) {
|
||||
case 'index':
|
||||
case '':
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->display($this->getTemplateResource('index.tpl'));
|
||||
break;
|
||||
default:
|
||||
parent::display($args, $request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pub ID type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPubIdType()
|
||||
{
|
||||
return 'doi';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pub ID display type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPubIdDisplayType()
|
||||
{
|
||||
return 'DOI';
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark selected submissions or issues as registered.
|
||||
*
|
||||
* @param Journal $context
|
||||
* @param array $objects Array of published submissions, issues or galleys
|
||||
*/
|
||||
public function markRegistered($context, $objects)
|
||||
{
|
||||
foreach ($objects as $object) {
|
||||
$doiId = $object->getData('doiId');
|
||||
|
||||
if ($doiId != null) {
|
||||
Repo::doi()->markRegistered($doiId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saving object's DOI to the object's
|
||||
* "registeredDoi" setting.
|
||||
* We prefix the setting with the plugin's
|
||||
* id so that we do not get name clashes
|
||||
* when several DOI registration plug-ins
|
||||
* are active at the same time.
|
||||
*
|
||||
* @param Journal $context
|
||||
* @param Issue|Submission|Galley $object
|
||||
* @param string $testPrefix
|
||||
*/
|
||||
public function saveRegisteredDoi($context, $object, $testPrefix = '10.1234')
|
||||
{
|
||||
$registeredDoi = $object->getStoredPubId('doi');
|
||||
assert(!empty($registeredDoi));
|
||||
if ($this->isTestMode($context)) {
|
||||
$registeredDoi = PKPString::regexp_replace('#^[^/]+/#', $testPrefix . '/', $registeredDoi);
|
||||
}
|
||||
$object->setData($this->getPluginSettingsPrefix() . '::' . DOI_EXPORT_REGISTERED_DOI, $registeredDoi);
|
||||
$this->updateObject($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of additional setting names that should be stored with the objects.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _getObjectAdditionalSettings()
|
||||
{
|
||||
return array_merge(parent::_getObjectAdditionalSettings(), [
|
||||
$this->getPluginSettingsPrefix() . '::' . DOI_EXPORT_REGISTERED_DOI
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get published submissions with a DOI assigned from submission IDs.
|
||||
*
|
||||
* @param array $submissionIds
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPublishedSubmissions($submissionIds, $context)
|
||||
{
|
||||
$allSubmissionIds = Repo::submission()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByStatus([PKPSubmission::STATUS_PUBLISHED])
|
||||
->getIds()
|
||||
->toArray();
|
||||
$validSubmissionIds = array_intersect($allSubmissionIds, $submissionIds);
|
||||
$submissions = array_map(function ($submissionId) {
|
||||
return Repo::submission()->get($submissionId);
|
||||
}, $validSubmissionIds);
|
||||
return array_filter($submissions, function ($submission) {
|
||||
return $submission->getCurrentPublication()->getDoi() !== null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get published issues with a DOI assigned from issue IDs.
|
||||
*
|
||||
* @param array $issueIds
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPublishedIssues($issueIds, $context)
|
||||
{
|
||||
return Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByIssueIds($issueIds)
|
||||
->filterByPublished(true)
|
||||
->filterByHasDois(true)
|
||||
->getMany()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get article galleys with a DOI assigned from galley IDs.
|
||||
*
|
||||
* @param array $galleyIds
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArticleGalleys($galleyIds, $context)
|
||||
{
|
||||
$allGalleyIds = Repo::galley()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getIds()
|
||||
->toArray();
|
||||
$validGalleyIds = array_intersect($allGalleyIds, $galleyIds);
|
||||
$galleys = array_map(function ($galleyId) {
|
||||
return Repo::galley()->get($galleyId);
|
||||
}, $validGalleyIds);
|
||||
return array_filter($galleys, function ($galley) {
|
||||
return $galley->getDoi() !== null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc ImportExportPlugin::executeCLI()
|
||||
*/
|
||||
public function executeCLI($scriptName, &$args)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc ImportExportPlugin::supportsCLI()
|
||||
*/
|
||||
public function supportsCLI(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\plugins\DOIPubIdExportPlugin', '\DOIPubIdExportPlugin');
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/IDoiRegistrationAgency.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class IDoiRegistrationAgency
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Interface that registration agency plugins must implement to support DOI registrations.
|
||||
*/
|
||||
|
||||
namespace APP\plugins;
|
||||
|
||||
use APP\issue\Issue;
|
||||
use APP\submission\Submission;
|
||||
use PKP\context\Context;
|
||||
use PKP\plugins\IPKPDoiRegistrationAgency;
|
||||
|
||||
interface IDoiRegistrationAgency extends IPKPDoiRegistrationAgency
|
||||
{
|
||||
/**
|
||||
* @param Submission[] $submissions
|
||||
*
|
||||
*/
|
||||
public function exportSubmissions(array $submissions, Context $context): array;
|
||||
|
||||
/**
|
||||
* @param Submission[] $submissions
|
||||
*
|
||||
*/
|
||||
public function depositSubmissions(array $submissions, Context $context): array;
|
||||
|
||||
/**
|
||||
* @param Issue[] $issues
|
||||
*
|
||||
*/
|
||||
public function exportIssues(array $issues, Context $context): array;
|
||||
|
||||
/**
|
||||
* @param Issue[] $issues
|
||||
*
|
||||
*/
|
||||
public function depositIssues(array $issues, Context $context): array;
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PubIdPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PubIdPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Public identifiers plugins common functions
|
||||
*/
|
||||
|
||||
namespace APP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Collector;
|
||||
use APP\issue\Issue;
|
||||
use APP\journal\Journal;
|
||||
use APP\notification\NotificationManager;
|
||||
use APP\submission\Submission;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\core\PKPString;
|
||||
use PKP\submission\Representation;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
abstract class PubIdPlugin extends \PKP\plugins\PKPPubIdPlugin
|
||||
{
|
||||
/**
|
||||
* @copydoc Plugin::manage()
|
||||
*/
|
||||
public function manage($args, $request)
|
||||
{
|
||||
$user = $request->getUser();
|
||||
$router = $request->getRouter();
|
||||
$context = $router->getContext($request);
|
||||
|
||||
$notificationManager = new NotificationManager();
|
||||
switch ($request->getUserVar('verb')) {
|
||||
case 'assignPubIds':
|
||||
if (!$request->checkCSRF()) {
|
||||
return new JSONMessage(false);
|
||||
}
|
||||
return $this->assignPubIds($request, $context);
|
||||
default:
|
||||
return parent::manage($args, $request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles pubId assignment for any publication, galley, or issue pubIds
|
||||
* (usen on the plugin setting page, called in the plugin manage function)
|
||||
*/
|
||||
protected function assignPubIds($request, $context): JSONMessage
|
||||
{
|
||||
$suffixFieldName = $this->getSuffixFieldName();
|
||||
$suffixGenerationStrategy = $this->getSetting($context->getId(), $suffixFieldName);
|
||||
if ($suffixGenerationStrategy != 'customId') {
|
||||
$issueEnabled = $this->isObjectTypeEnabled('Issue', $context->getId());
|
||||
$publicationEnabled = $this->isObjectTypeEnabled('Publication', $context->getId());
|
||||
$representationEnabled = $this->isObjectTypeEnabled('Representation', $context->getId());
|
||||
if ($issueEnabled) {
|
||||
$issues = Repo::issue()->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByPublished(true)
|
||||
->orderBy(Collector::ORDERBY_PUBLISHED_ISSUES)
|
||||
->getMany();
|
||||
foreach ($issues as $issue) {
|
||||
$issuePubId = $issue->getStoredPubId($this->getPubIdType());
|
||||
if (empty($issuePubId)) {
|
||||
$issuePubId = $this->getPubId($issue);
|
||||
Repo::issue()->dao->changePubId($issue->getId(), $this->getPubIdType(), $issuePubId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($publicationEnabled || $representationEnabled) {
|
||||
$representationDao = Application::getRepresentationDAO();
|
||||
$submissions = Repo::submission()->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByStatus([Submission::STATUS_PUBLISHED])
|
||||
->getMany();
|
||||
|
||||
foreach ($submissions as $submission) {
|
||||
$publications = $submission->getData('publications');
|
||||
if ($publicationEnabled) {
|
||||
foreach ($publications as $publication) {
|
||||
$publicationPubId = $publication->getStoredPubId($this->getPubIdType());
|
||||
if (empty($publicationPubId)) {
|
||||
$publicationPubId = $this->getPubId($publication);
|
||||
Repo::publication()->dao->changePubId(
|
||||
$publication->getId(),
|
||||
$this->getPubIdType(),
|
||||
$publicationPubId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($representationEnabled) {
|
||||
foreach ($publications as $publication) {
|
||||
$representations = Repo::galley()->getCollector()
|
||||
->filterByPublicationIds([$publication->getId()])
|
||||
->getMany();
|
||||
|
||||
foreach ($representations as $representation) {
|
||||
$representationPubId = $representation->getStoredPubId($this->getPubIdType());
|
||||
if (empty($representationPubId)) {
|
||||
$representationPubId = $this->getPubId($representation);
|
||||
$representationDao->changePubId(
|
||||
$representation->getId(),
|
||||
$this->getPubIdType(),
|
||||
$representationPubId
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new JSONMessage(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Protected template methods from PKPPlubIdPlugin
|
||||
//
|
||||
/**
|
||||
* @copydoc PKPPubIdPlugin::getPubObjectTypes()
|
||||
*/
|
||||
public function getPubObjectTypes()
|
||||
{
|
||||
$pubObjectTypes = parent::getPubObjectTypes();
|
||||
$pubObjectTypes['Issue'] = 'APP\issue\Issue';
|
||||
return $pubObjectTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPlugin::checkDuplicate()
|
||||
*/
|
||||
public function checkDuplicate($pubId, $pubObjectType, $excludeId, $contextId)
|
||||
{
|
||||
foreach ($this->getPubObjectTypes() as $type => $fqcn) {
|
||||
if ($type === 'Issue') {
|
||||
$excludeTypeId = $type === $pubObjectType ? $excludeId : null;
|
||||
if (Repo::issue()->dao->pubIdExists($this->getPubIdType(), $pubId, $excludeTypeId, $contextId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::checkDuplicate($pubId, $pubObjectType, $excludeId, $contextId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public identifier.
|
||||
*
|
||||
* @param object $pubObject
|
||||
* Publication, Representation, SubmissionFile, Issue
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPubId($pubObject)
|
||||
{
|
||||
// Get the pub id type
|
||||
$pubIdType = $this->getPubIdType();
|
||||
|
||||
// If we already have an assigned pub id, use it.
|
||||
$storedPubId = $pubObject->getStoredPubId($pubIdType);
|
||||
if ($storedPubId) {
|
||||
return $storedPubId;
|
||||
}
|
||||
|
||||
// Determine the type of the publishing object.
|
||||
$pubObjectType = $this->getPubObjectType($pubObject);
|
||||
|
||||
// Initialize variables for publication objects.
|
||||
$issue = ($pubObjectType == 'Issue' ? $pubObject : null);
|
||||
$submission = null;
|
||||
// Publication is actually handled differently now, but keep it here however for now.
|
||||
$publication = ($pubObjectType == 'Publication' ? $pubObject : null);
|
||||
$representation = ($pubObjectType == 'Representation' ? $pubObject : null);
|
||||
$submissionFile = ($pubObjectType == 'SubmissionFile' ? $pubObject : null);
|
||||
|
||||
// Get the context id.
|
||||
if ($pubObjectType === 'Issue') {
|
||||
$contextId = $pubObject->getJournalId();
|
||||
} elseif ($pubObjectType === 'Representation') {
|
||||
$publication = Repo::publication()->get($pubObject->getData('publicationId'));
|
||||
$submission = Repo::submission()->get($publication->getData('submissionId'));
|
||||
$contextId = $submission->getData('contextId');
|
||||
} elseif (in_array($pubObjectType, ['Publication', 'SubmissionFile'])) {
|
||||
$submission = Repo::submission()->get($pubObject->getData('submissionId'));
|
||||
$contextId = $submission->getData('contextId');
|
||||
}
|
||||
|
||||
// Check the context
|
||||
$context = $this->getContext($contextId);
|
||||
if (!$context) {
|
||||
return null;
|
||||
}
|
||||
$contextId = $context->getId();
|
||||
|
||||
// Check whether pub ids are enabled for the given object type.
|
||||
$objectTypeEnabled = $this->isObjectTypeEnabled($pubObjectType, $contextId);
|
||||
if (!$objectTypeEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Retrieve the issue.
|
||||
if (!$pubObject instanceof Issue) {
|
||||
assert(!is_null($submission));
|
||||
$issue = Repo::issue()->getBySubmissionId($submission->getId());
|
||||
$issue = $issue->getJournalId() == $contextId ? $issue : null;
|
||||
}
|
||||
if ($issue && $contextId != $issue->getJournalId()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Retrieve the pub id prefix.
|
||||
$pubIdPrefix = $this->getSetting($contextId, $this->getPrefixFieldName());
|
||||
if (empty($pubIdPrefix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generate the pub id suffix.
|
||||
$suffixFieldName = $this->getSuffixFieldName();
|
||||
$suffixGenerationStrategy = $this->getSetting($contextId, $suffixFieldName);
|
||||
switch ($suffixGenerationStrategy) {
|
||||
case 'customId':
|
||||
$pubIdSuffix = $pubObject->getData($suffixFieldName);
|
||||
break;
|
||||
|
||||
case 'pattern':
|
||||
$suffixPatternsFieldNames = $this->getSuffixPatternsFieldNames();
|
||||
$pubIdSuffix = $this->getSetting($contextId, $suffixPatternsFieldNames[$pubObjectType]);
|
||||
|
||||
$pubIdSuffix = $this->generateCustomPattern($context, $pubIdSuffix, $pubObject, $issue, $submission, $representation, $submissionFile);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$pubIdSuffix = $this::generateDefaultPattern($context, $issue, $submission, $representation, $submissionFile);
|
||||
}
|
||||
if (empty($pubIdSuffix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Construct the pub id from prefix and suffix.
|
||||
$pubId = $this->constructPubId($pubIdPrefix, $pubIdSuffix, $contextId);
|
||||
|
||||
return $pubId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the default, semantic-based pub-id pattern suffix
|
||||
*
|
||||
* @param Journal $context
|
||||
* @param ?Issue $issue
|
||||
* @param Submission $submission
|
||||
* @param Representation $representation
|
||||
* @param SubmissionFile $submissionFile
|
||||
*
|
||||
*/
|
||||
public static function generateDefaultPattern($context, $issue = null, $submission = null, $representation = null, $submissionFile = null): string
|
||||
{
|
||||
$pubIdSuffix = PKPString::regexp_replace('/[^-._;()\/A-Za-z0-9]/', '', PKPString::strtolower($context->getAcronym($context->getPrimaryLocale())));
|
||||
|
||||
if ($issue) {
|
||||
$pubIdSuffix .= '.v' . $issue->getVolume() . 'i' . $issue->getNumber();
|
||||
} else {
|
||||
$pubIdSuffix .= '.v%vi%i';
|
||||
}
|
||||
|
||||
if ($submission) {
|
||||
$pubIdSuffix .= '.' . $submission->getId();
|
||||
}
|
||||
|
||||
if ($representation) {
|
||||
$pubIdSuffix .= '.g' . $representation->getId();
|
||||
}
|
||||
|
||||
if ($submissionFile) {
|
||||
$pubIdSuffix .= '.f' . $submissionFile->getId();
|
||||
}
|
||||
|
||||
return $pubIdSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the custom, user-defined pub-id pattern suffix
|
||||
*
|
||||
* @param Journal $context
|
||||
* @param string $pubIdSuffix
|
||||
* @param object $pubObject
|
||||
* @param Issue $issue
|
||||
* @param Submission $submission
|
||||
* @param Representation $representation
|
||||
* @param SubmissionFile $submissionFile
|
||||
*
|
||||
*/
|
||||
public static function generateCustomPattern($context, $pubIdSuffix, $pubObject, $issue = null, $submission = null, $representation = null, $submissionFile = null): string
|
||||
{
|
||||
// %j - journal initials, remove special characters and uncapitalize
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%j/', PKPString::regexp_replace('/[^-._;()\/A-Za-z0-9]/', '', PKPString::strtolower($context->getAcronym($context->getPrimaryLocale()))), $pubIdSuffix);
|
||||
|
||||
// %x - custom identifier
|
||||
if ($pubObject->getStoredPubId('publisher-id')) {
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%x/', $pubObject->getStoredPubId('publisher-id'), $pubIdSuffix);
|
||||
}
|
||||
|
||||
if ($issue) {
|
||||
// %v - volume number
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%v/', $issue->getVolume(), $pubIdSuffix);
|
||||
// %i - issue number
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%i/', $issue->getNumber(), $pubIdSuffix);
|
||||
// %Y - year
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%Y/', $issue->getYear(), $pubIdSuffix);
|
||||
}
|
||||
|
||||
if ($submission) {
|
||||
// %a - article id
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%a/', $submission->getId(), $pubIdSuffix);
|
||||
// %p - page number
|
||||
if ($submission->getPages()) {
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%p/', $submission->getPages(), $pubIdSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
if ($representation) {
|
||||
// %g - galley id
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%g/', $representation->getId(), $pubIdSuffix);
|
||||
}
|
||||
|
||||
if ($submissionFile) {
|
||||
// %f - file id
|
||||
$pubIdSuffix = PKPString::regexp_replace('/%f/', $submissionFile->getId(), $pubIdSuffix);
|
||||
}
|
||||
|
||||
return $pubIdSuffix;
|
||||
}
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
/**
|
||||
* Clear pubIds of all issue objects.
|
||||
*
|
||||
* @param Issue $issue
|
||||
*/
|
||||
public function clearIssueObjectsPubIds($issue)
|
||||
{
|
||||
$publicationPubIdEnabled = $this->isObjectTypeEnabled('Publication', $issue->getJournalId());
|
||||
$representationPubIdEnabled = $this->isObjectTypeEnabled('Representation', $issue->getJournalId());
|
||||
$filePubIdEnabled = $this->isObjectTypeEnabled('SubmissionFile', $issue->getJournalId());
|
||||
if (!$publicationPubIdEnabled && !$representationPubIdEnabled && !$filePubIdEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$pubIdType = $this->getPubIdType();
|
||||
|
||||
$submissionIds = Repo::submission()
|
||||
->getCollector()
|
||||
->filterByContextIds([$issue->getJournalId()])
|
||||
->filterByIssueIds([$issue->getId()])
|
||||
->getIds();
|
||||
|
||||
foreach ($submissionIds as $submissionId) {
|
||||
$submission = Repo::submission()->get($submissionId);
|
||||
if ($publicationPubIdEnabled) { // Does this option have to be enabled here for?
|
||||
foreach ($submission->getData('publications') as $publication) {
|
||||
Repo::publication()->dao->deletePubId($publication->getId(), $pubIdType);
|
||||
}
|
||||
}
|
||||
if ($representationPubIdEnabled || $filePubIdEnabled) { // Does this option have to be enabled here for?
|
||||
foreach ($submission->getData('publications') as $publication) {
|
||||
$representations = Application::getRepresentationDAO()->getByPublicationId($publication->getId());
|
||||
foreach ($representations as $representation) {
|
||||
if ($representationPubIdEnabled) { // Does this option have to be enabled here for?
|
||||
Application::getRepresentationDAO()->deletePubId($representation->getId(), $pubIdType);
|
||||
}
|
||||
if ($filePubIdEnabled) { // Does this option have to be enabled here for?
|
||||
$articleProofFileIds = Repo::submissionFile()
|
||||
->getCollector()
|
||||
->filterByAssoc(
|
||||
Application::ASSOC_TYPE_REPRESENTATION,
|
||||
[$representation->getId()]
|
||||
)->filterByFileStages([SubmissionFile::SUBMISSION_FILE_PROOF])
|
||||
->getIds();
|
||||
|
||||
foreach ($articleProofFileIds as $articleProofFileId) {
|
||||
Repo::submissionFile()->dao->deletePubId($articleProofFileId, $pubIdType);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($representations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPPubIdPlugin::getDAOs()
|
||||
*/
|
||||
public function getDAOs()
|
||||
{
|
||||
return array_merge(parent::getDAOs(), [Repo::issue()->dao]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\plugins\PubIdPlugin', '\PubIdPlugin');
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PubObjectCache.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PubObjectCache
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief A cache for publication objects required during export.
|
||||
*/
|
||||
|
||||
namespace APP\plugins;
|
||||
|
||||
use APP\issue\Issue;
|
||||
use APP\submission\Submission;
|
||||
use PKP\galley\Galley;
|
||||
use PKP\submission\Genre;
|
||||
|
||||
class PubObjectCache
|
||||
{
|
||||
/** @var array */
|
||||
public $_objectCache = [];
|
||||
|
||||
|
||||
//
|
||||
// Public API
|
||||
//
|
||||
/**
|
||||
* Add a publishing object to the cache.
|
||||
*
|
||||
* @param Issue|Submission|Galley|Genre $object
|
||||
* @param Submission|null $parent Only required when adding a galley.
|
||||
*/
|
||||
public function add($object, $parent)
|
||||
{
|
||||
if ($object instanceof Issue) {
|
||||
$this->_insertInternally($object, 'issues', $object->getId());
|
||||
}
|
||||
if ($object instanceof Submission) {
|
||||
$this->_insertInternally($object, 'articles', $object->getId());
|
||||
$this->_insertInternally($object, 'articlesByIssue', $object->getCurrentPublication()->getData('issueId'), $object->getId());
|
||||
}
|
||||
if ($object instanceof Galley) {
|
||||
assert($parent instanceof Submission);
|
||||
$this->_insertInternally($object, 'galleys', $object->getId());
|
||||
$this->_insertInternally($object, 'galleysByArticle', $object->getData('submissionId'), $object->getId());
|
||||
$this->_insertInternally($object, 'galleysByIssue', $parent->getCurrentPublication()->getData('issueId'), $object->getId());
|
||||
}
|
||||
if ($object instanceof Genre) {
|
||||
$this->_insertInternally($object, 'genres', $object->getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the given cache id "complete", i.e. it
|
||||
* contains all child objects for the given object
|
||||
* id.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @param string $objectId
|
||||
*/
|
||||
public function markComplete($cacheId, $objectId)
|
||||
{
|
||||
assert(is_array($this->_objectCache[$cacheId][$objectId]));
|
||||
$this->_objectCache[$cacheId][$objectId]['complete'] = true;
|
||||
|
||||
// Order objects in the completed cache by ID.
|
||||
ksort($this->_objectCache[$cacheId][$objectId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve (an) object(s) from the cache.
|
||||
*
|
||||
* NB: You must check whether an object is in the cache
|
||||
* before you try to retrieve it with this method.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @param int $id1
|
||||
* @param int $id2
|
||||
*
|
||||
*/
|
||||
public function get($cacheId, $id1, $id2 = null)
|
||||
{
|
||||
assert($this->isCached($cacheId, $id1, $id2));
|
||||
if (is_null($id2)) {
|
||||
$returner = $this->_objectCache[$cacheId][$id1];
|
||||
if (is_array($returner)) {
|
||||
unset($returner['complete']);
|
||||
}
|
||||
return $returner;
|
||||
} else {
|
||||
return $this->_objectCache[$cacheId][$id1][$id2];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given object is in the cache.
|
||||
*
|
||||
* @param string $cacheId
|
||||
* @param int $id1
|
||||
* @param int $id2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCached($cacheId, $id1, $id2 = null)
|
||||
{
|
||||
if (!isset($this->_objectCache[$cacheId])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$id1 = (int)$id1;
|
||||
if (is_null($id2)) {
|
||||
if (!isset($this->_objectCache[$cacheId][$id1])) {
|
||||
return false;
|
||||
}
|
||||
if (is_array($this->_objectCache[$cacheId][$id1])) {
|
||||
return isset($this->_objectCache[$cacheId][$id1]['complete']);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
$id2 = (int)$id2;
|
||||
return isset($this->_objectCache[$cacheId][$id1][$id2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private helper methods
|
||||
//
|
||||
/**
|
||||
* Insert an object into the cache.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $cacheId
|
||||
* @param int $id1
|
||||
* @param int $id2
|
||||
*/
|
||||
public function _insertInternally($object, $cacheId, $id1, $id2 = null)
|
||||
{
|
||||
if ($this->isCached($cacheId, $id1, $id2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset($this->_objectCache[$cacheId])) {
|
||||
$this->_objectCache[$cacheId] = [];
|
||||
}
|
||||
|
||||
$id1 = (int)$id1;
|
||||
if (is_null($id2)) {
|
||||
$this->_objectCache[$cacheId][$id1] = $object;
|
||||
} else {
|
||||
$id2 = (int)$id2;
|
||||
if (!isset($this->_objectCache[$cacheId][$id1])) {
|
||||
$this->_objectCache[$cacheId][$id1] = [];
|
||||
}
|
||||
$this->_objectCache[$cacheId][$id1][$id2] = $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\plugins\PubObjectCache', '\PubObjectCache');
|
||||
}
|
||||
@@ -0,0 +1,935 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file classes/plugins/PubObjectsExportPlugin.php
|
||||
*
|
||||
* Copyright (c) 2014-2021 Simon Fraser University
|
||||
* Copyright (c) 2003-2021 John Willinsky
|
||||
* Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
|
||||
*
|
||||
* @class PubObjectsExportPlugin
|
||||
*
|
||||
* @ingroup plugins
|
||||
*
|
||||
* @brief Basis class for XML metadata export plugins
|
||||
*/
|
||||
|
||||
namespace APP\plugins;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Request;
|
||||
use APP\facades\Repo;
|
||||
use APP\issue\Issue;
|
||||
use APP\journal\Journal;
|
||||
use APP\journal\JournalDAO;
|
||||
use APP\notification\NotificationManager;
|
||||
use APP\submission\Submission;
|
||||
use APP\template\TemplateManager;
|
||||
use PKP\core\EntityDAO;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\db\DAO;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\db\SchemaDAO;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\filter\FilterDAO;
|
||||
use PKP\galley\Galley;
|
||||
use PKP\linkAction\LinkAction;
|
||||
use PKP\linkAction\request\NullAction;
|
||||
use PKP\notification\PKPNotification;
|
||||
use PKP\plugins\Hook;
|
||||
use PKP\plugins\importexport\PKPImportExportDeployment;
|
||||
use PKP\plugins\ImportExportPlugin;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\submission\PKPSubmission;
|
||||
use PKP\user\User;
|
||||
|
||||
// The statuses.
|
||||
define('EXPORT_STATUS_ANY', '');
|
||||
define('EXPORT_STATUS_NOT_DEPOSITED', 'notDeposited');
|
||||
define('EXPORT_STATUS_MARKEDREGISTERED', 'markedRegistered');
|
||||
define('EXPORT_STATUS_REGISTERED', 'registered');
|
||||
|
||||
// The actions.
|
||||
define('EXPORT_ACTION_EXPORT', 'export');
|
||||
define('EXPORT_ACTION_MARKREGISTERED', 'markRegistered');
|
||||
define('EXPORT_ACTION_DEPOSIT', 'deposit');
|
||||
|
||||
// Configuration errors.
|
||||
define('EXPORT_CONFIG_ERROR_SETTINGS', 0x02);
|
||||
|
||||
abstract class PubObjectsExportPlugin extends ImportExportPlugin
|
||||
{
|
||||
public const EXPORT_ACTION_EXPORT = 'export';
|
||||
public const EXPORT_ACTION_MARKREGISTERED = 'markRegistered';
|
||||
public const EXPORT_ACTION_DEPOSIT = 'deposit';
|
||||
|
||||
/** @var PubObjectCache */
|
||||
public $_cache;
|
||||
|
||||
/**
|
||||
* Get the plugin cache
|
||||
*
|
||||
* @return PubObjectCache
|
||||
*/
|
||||
public function getCache()
|
||||
{
|
||||
if (!$this->_cache instanceof PubObjectCache) {
|
||||
// Instantiate the cache.
|
||||
$this->_cache = new PubObjectCache();
|
||||
}
|
||||
return $this->_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
if (!parent::register($category, $path, $mainContextId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Application::isUnderMaintenance()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->addLocaleData();
|
||||
|
||||
Hook::add('AcronPlugin::parseCronTab', [$this, 'callbackParseCronTab']);
|
||||
foreach ($this->_getDAOs() as $dao) {
|
||||
if ($dao instanceof SchemaDAO) {
|
||||
Hook::add('Schema::get::' . $dao->schemaName, [$this, 'addToSchema']);
|
||||
} elseif ($dao instanceof EntityDAO) {
|
||||
Hook::add('Schema::get::' . $dao->schema, [$this, 'addToSchema']);
|
||||
} else {
|
||||
Hook::add(strtolower_codesafe(get_class($dao)) . '::getAdditionalFieldNames', [&$this, 'getAdditionalFieldNames']);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::manage()
|
||||
*/
|
||||
public function manage($args, $request)
|
||||
{
|
||||
$user = $request->getUser();
|
||||
$router = $request->getRouter();
|
||||
$context = $router->getContext($request);
|
||||
|
||||
$form = $this->_instantiateSettingsForm($context);
|
||||
$notificationManager = new NotificationManager();
|
||||
switch ($request->getUserVar('verb')) {
|
||||
case 'save':
|
||||
$form->readInputData();
|
||||
if ($form->validate()) {
|
||||
$form->execute();
|
||||
$notificationManager->createTrivialNotification($user->getId(), PKPNotification::NOTIFICATION_TYPE_SUCCESS);
|
||||
return new JSONMessage(true);
|
||||
} else {
|
||||
return new JSONMessage(true, $form->fetch($request));
|
||||
}
|
||||
// no break
|
||||
case 'index':
|
||||
$form->initData();
|
||||
return new JSONMessage(true, $form->fetch($request));
|
||||
case 'statusMessage':
|
||||
$statusMessage = $this->getStatusMessage($request);
|
||||
if ($statusMessage) {
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->assign([
|
||||
'statusMessage' => htmlentities($statusMessage),
|
||||
]);
|
||||
return new JSONMessage(true, $templateMgr->fetch($this->getTemplateResource('statusMessage.tpl')));
|
||||
}
|
||||
}
|
||||
return parent::manage($args, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc ImportExportPlugin::display()
|
||||
*/
|
||||
public function display($args, $request)
|
||||
{
|
||||
parent::display($args, $request);
|
||||
|
||||
$context = $request->getContext();
|
||||
switch (array_shift($args)) {
|
||||
case 'index':
|
||||
case '':
|
||||
// Check for configuration errors:
|
||||
$configurationErrors = [];
|
||||
// missing plugin settings
|
||||
$form = $this->_instantiateSettingsForm($context);
|
||||
foreach ($form->getFormFields() as $fieldName => $fieldType) {
|
||||
if ($form->isOptional($fieldName)) {
|
||||
continue;
|
||||
}
|
||||
$pluginSetting = $this->getSetting($context->getId(), $fieldName);
|
||||
if (empty($pluginSetting)) {
|
||||
$configurationErrors[] = EXPORT_CONFIG_ERROR_SETTINGS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add link actions
|
||||
$actions = $this->getExportActions($context);
|
||||
$actionNames = array_intersect_key($this->getExportActionNames(), array_flip($actions));
|
||||
$linkActions = [];
|
||||
foreach ($actionNames as $action => $actionName) {
|
||||
$linkActions[] = new LinkAction($action, new NullAction(), $actionName);
|
||||
}
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
$templateMgr->assign([
|
||||
'plugin' => $this,
|
||||
'actionNames' => $actionNames,
|
||||
'configurationErrors' => $configurationErrors,
|
||||
]);
|
||||
break;
|
||||
case 'exportSubmissions':
|
||||
case 'exportIssues':
|
||||
case 'exportRepresentations':
|
||||
$this->prepareAndExportPubObjects($request, $context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers relevant pub objects and runs export action
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Journal $context
|
||||
* @param array $args Optional args for passing in submissionIds from external API calls
|
||||
*/
|
||||
public function prepareAndExportPubObjects($request, $context, $args = [])
|
||||
{
|
||||
$selectedSubmissions = (array) $request->getUserVar('selectedSubmissions');
|
||||
$selectedIssues = (array) $request->getUserVar('selectedIssues');
|
||||
$selectedRepresentations = (array) $request->getUserVar('selectedRepresentations');
|
||||
$tab = (string) $request->getUserVar('tab');
|
||||
$noValidation = $request->getUserVar('validation') ? false : true;
|
||||
|
||||
if (!empty($args['submissionIds'])) {
|
||||
$selectedSubmissions = (array) $args['submissionIds'];
|
||||
}
|
||||
if (!empty($args['issueIds'])) {
|
||||
$selectedIssues = (array) $args['issueIds'];
|
||||
}
|
||||
if (empty($selectedSubmissions) && empty($selectedIssues) && empty($selectedRepresentations)) {
|
||||
fatalError(__('plugins.importexport.common.error.noObjectsSelected'));
|
||||
}
|
||||
if (!empty($selectedSubmissions)) {
|
||||
$objects = $this->getPublishedSubmissions($selectedSubmissions, $context);
|
||||
$filter = $this->getSubmissionFilter();
|
||||
$objectsFileNamePart = 'articles';
|
||||
} elseif (!empty($selectedIssues)) {
|
||||
$objects = $this->getPublishedIssues($selectedIssues, $context);
|
||||
$filter = $this->getIssueFilter();
|
||||
$objectsFileNamePart = 'issues';
|
||||
} elseif (!empty($selectedRepresentations)) {
|
||||
$objects = $this->getArticleGalleys($selectedRepresentations, $context);
|
||||
$filter = $this->getRepresentationFilter();
|
||||
$objectsFileNamePart = 'galleys';
|
||||
}
|
||||
|
||||
// Execute export action
|
||||
$this->executeExportAction($request, $objects, $filter, $tab, $objectsFileNamePart, $noValidation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute export action.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param array $objects Array of objects to be exported
|
||||
* @param string $filter Filter to use
|
||||
* @param string $tab Tab to return to
|
||||
* @param string $objectsFileNamePart Export file name part for this kind of objects
|
||||
* @param bool $noValidation If set to true no XML validation will be done
|
||||
* @param bool $shouldRedirect If set to true, will redirect to `$tab`. Should be true if executed within an ImportExportPlugin.
|
||||
*/
|
||||
public function executeExportAction($request, $objects, $filter, $tab, $objectsFileNamePart, $noValidation = null, $shouldRedirect = true)
|
||||
{
|
||||
$context = $request->getContext();
|
||||
$path = ['plugin', $this->getName()];
|
||||
if ($this->_checkForExportAction(EXPORT_ACTION_EXPORT)) {
|
||||
assert($filter != null);
|
||||
|
||||
$onlyValidateExport = ($request->getUserVar('onlyValidateExport')) ? true : false;
|
||||
if ($onlyValidateExport) {
|
||||
$noValidation = false;
|
||||
}
|
||||
|
||||
// Get the XML
|
||||
$exportXml = $this->exportXML($objects, $filter, $context, $noValidation);
|
||||
|
||||
if ($onlyValidateExport) {
|
||||
if (isset($exportXml)) {
|
||||
$this->_sendNotification(
|
||||
$request->getUser(),
|
||||
'plugins.importexport.common.validation.success',
|
||||
PKPNotification::NOTIFICATION_TYPE_SUCCESS
|
||||
);
|
||||
} else {
|
||||
$this->_sendNotification(
|
||||
$request->getUser(),
|
||||
'plugins.importexport.common.validation.fail',
|
||||
PKPNotification::NOTIFICATION_TYPE_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
if ($shouldRedirect) {
|
||||
$request->redirect(null, null, null, $path, null, $tab);
|
||||
}
|
||||
} else {
|
||||
$fileManager = new FileManager();
|
||||
$exportFileName = $this->getExportFileName($this->getExportPath(), $objectsFileNamePart, $context, '.xml');
|
||||
$fileManager->writeFile($exportFileName, $exportXml);
|
||||
$fileManager->downloadByPath($exportFileName);
|
||||
$fileManager->deleteByPath($exportFileName);
|
||||
}
|
||||
} elseif ($this->_checkForExportAction(EXPORT_ACTION_DEPOSIT)) {
|
||||
assert($filter != null);
|
||||
// Get the XML
|
||||
$exportXml = $this->exportXML($objects, $filter, $context, $noValidation);
|
||||
// Write the XML to a file.
|
||||
// export file name example: crossref-20160723-160036-articles-1.xml
|
||||
$fileManager = new FileManager();
|
||||
$exportFileName = $this->getExportFileName($this->getExportPath(), $objectsFileNamePart, $context, '.xml');
|
||||
$fileManager->writeFile($exportFileName, $exportXml);
|
||||
// Deposit the XML file.
|
||||
$result = $this->depositXML($objects, $context, $exportFileName);
|
||||
// send notifications
|
||||
if ($result === true) {
|
||||
$this->_sendNotification(
|
||||
$request->getUser(),
|
||||
$this->getDepositSuccessNotificationMessageKey(),
|
||||
PKPNotification::NOTIFICATION_TYPE_SUCCESS
|
||||
);
|
||||
} else {
|
||||
if (is_array($result)) {
|
||||
foreach ($result as $error) {
|
||||
assert(is_array($error) && count($error) >= 1);
|
||||
$this->_sendNotification(
|
||||
$request->getUser(),
|
||||
$error[0],
|
||||
PKPNotification::NOTIFICATION_TYPE_ERROR,
|
||||
($error[1] ?? null)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove all temporary files.
|
||||
$fileManager->deleteByPath($exportFileName);
|
||||
if ($shouldRedirect) {
|
||||
// redirect back to the right tab
|
||||
$request->redirect(null, null, null, $path, null, $tab);
|
||||
}
|
||||
} elseif ($this->_checkForExportAction(EXPORT_ACTION_MARKREGISTERED)) {
|
||||
$this->markRegistered($context, $objects);
|
||||
if ($shouldRedirect) {
|
||||
// redirect back to the right tab
|
||||
$request->redirect(null, null, null, $path, null, $tab);
|
||||
}
|
||||
} else {
|
||||
$dispatcher = $request->getDispatcher();
|
||||
$dispatcher->handle404();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the locale key used in the notification for
|
||||
* the successful deposit.
|
||||
*/
|
||||
public function getDepositSuccessNotificationMessageKey()
|
||||
{
|
||||
return 'plugins.importexport.common.register.success';
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposit XML document.
|
||||
* This must be implemented in the subclasses, if the action is supported.
|
||||
*
|
||||
* @param mixed $objects Array of or single published submission, issue or galley
|
||||
* @param Journal $context
|
||||
* @param string $filename Export XML filename
|
||||
*
|
||||
* @return bool|array Whether the XML document has been registered
|
||||
*/
|
||||
abstract public function depositXML($objects, $context, $filename);
|
||||
|
||||
/**
|
||||
* Get detailed message of the object status i.e. failure messages.
|
||||
* Parameters needed have to be in the request object.
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return string Preformatted text that will be displayed in a div element in the modal
|
||||
*/
|
||||
public function getStatusMessage($request)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submission filter.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSubmissionFilter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the issue filter.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getIssueFilter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the representation filter.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRepresentationFilter()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status names for the filter search option.
|
||||
*
|
||||
* @return array (string status => string text)
|
||||
*/
|
||||
public function getStatusNames()
|
||||
{
|
||||
return [
|
||||
EXPORT_STATUS_ANY => __('plugins.importexport.common.status.any'),
|
||||
EXPORT_STATUS_NOT_DEPOSITED => __('plugins.importexport.common.status.notDeposited'),
|
||||
EXPORT_STATUS_MARKEDREGISTERED => __('plugins.importexport.common.status.markedRegistered'),
|
||||
EXPORT_STATUS_REGISTERED => __('plugins.importexport.common.status.registered'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status actions for the display to the user,
|
||||
* i.e. links to a web site with more information about the status.
|
||||
*
|
||||
* @param object $pubObject
|
||||
*
|
||||
* @return array (string status => link)
|
||||
*/
|
||||
public function getStatusActions($pubObject)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get actions.
|
||||
*
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExportActions($context)
|
||||
{
|
||||
$actions = [EXPORT_ACTION_EXPORT, EXPORT_ACTION_MARKREGISTERED];
|
||||
if ($this->getSetting($context->getId(), 'username') && $this->getSetting($context->getId(), 'password')) {
|
||||
array_unshift($actions, EXPORT_ACTION_DEPOSIT);
|
||||
}
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action names.
|
||||
*
|
||||
* @return array (string action => string text)
|
||||
*/
|
||||
public function getExportActionNames()
|
||||
{
|
||||
return [
|
||||
EXPORT_ACTION_DEPOSIT => __('plugins.importexport.common.action.register'),
|
||||
EXPORT_ACTION_EXPORT => __('plugins.importexport.common.action.export'),
|
||||
EXPORT_ACTION_MARKREGISTERED => __('plugins.importexport.common.action.markRegistered'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the plugin's deployment class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getExportDeploymentClassName();
|
||||
|
||||
/**
|
||||
* Get the XML for selected objects.
|
||||
*
|
||||
* @param mixed $objects Array of or single published submission, issue or galley
|
||||
* @param string $filter
|
||||
* @param Journal $context
|
||||
* @param bool $noValidation If set to true no XML validation will be done
|
||||
* @param null|mixed $outputErrors
|
||||
*
|
||||
* @return string XML document.
|
||||
*/
|
||||
public function exportXML($objects, $filter, $context, $noValidation = null, &$outputErrors = null)
|
||||
{
|
||||
$filterDao = DAORegistry::getDAO('FilterDAO'); /** @var FilterDAO $filterDao */
|
||||
$exportFilters = $filterDao->getObjectsByGroup($filter);
|
||||
assert(count($exportFilters) == 1); // Assert only a single serialization filter
|
||||
$exportFilter = array_shift($exportFilters);
|
||||
$exportDeployment = $this->_instantiateExportDeployment($context);
|
||||
$exportFilter->setDeployment($exportDeployment);
|
||||
if ($noValidation) {
|
||||
$exportFilter->setNoValidation($noValidation);
|
||||
}
|
||||
libxml_use_internal_errors(true);
|
||||
$exportXml = $exportFilter->execute($objects, true);
|
||||
$xml = $exportXml->saveXml();
|
||||
$errors = array_filter(libxml_get_errors(), function ($a) {
|
||||
return $a->level == LIBXML_ERR_ERROR || $a->level == LIBXML_ERR_FATAL;
|
||||
});
|
||||
if (!empty($errors)) {
|
||||
if ($outputErrors === null) {
|
||||
$this->displayXMLValidationErrors($errors, $xml);
|
||||
} else {
|
||||
$outputErrors = $errors;
|
||||
}
|
||||
}
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark selected submissions or issues as registered.
|
||||
*
|
||||
* @param Journal $context
|
||||
* @param array $objects Array of published submissions, issues or galleys
|
||||
*/
|
||||
public function markRegistered($context, $objects)
|
||||
{
|
||||
foreach ($objects as $object) {
|
||||
$object->setData($this->getDepositStatusSettingName(), EXPORT_STATUS_MARKEDREGISTERED);
|
||||
$this->updateObject($object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the given object.
|
||||
*
|
||||
* @param Issue|Submission|Galley $object
|
||||
*/
|
||||
protected function updateObject($object)
|
||||
{
|
||||
// Register a hook for the required additional
|
||||
// object fields. We do this on a temporary
|
||||
// basis as the hook adds a performance overhead
|
||||
// and the field will "stealthily" survive even
|
||||
// when the DAO does not know about it.
|
||||
$dao = $object->getDAO();
|
||||
$dao->update($object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add properties for this type of public identifier to the entity's list for
|
||||
* storage in the database.
|
||||
* This is used for non-SchemaDAO-backed entities only.
|
||||
*
|
||||
* @see PubObjectsExportPlugin::addToSchema()
|
||||
*
|
||||
* @param string $hookName
|
||||
* @param DAO $dao
|
||||
* @param array $additionalFields
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
public function getAdditionalFieldNames($hookName, $dao, &$additionalFields)
|
||||
{
|
||||
foreach ($this->_getObjectAdditionalSettings() as $fieldName) {
|
||||
$additionalFields[] = $fieldName;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add properties for this type of public identifier to the entity's list for
|
||||
* storage in the database.
|
||||
* This is used for SchemaDAO-backed entities only.
|
||||
*
|
||||
* @see PKPPubIdPlugin::getAdditionalFieldNames()
|
||||
*
|
||||
* @param string $hookName `Schema::get::publication`
|
||||
* @param array $params
|
||||
*/
|
||||
public function addToSchema($hookName, $params)
|
||||
{
|
||||
$schema = & $params[0];
|
||||
foreach ($this->_getObjectAdditionalSettings() as $fieldName) {
|
||||
$schema->properties->{$fieldName} = (object) [
|
||||
'type' => 'string',
|
||||
'apiSummary' => true,
|
||||
'validation' => ['nullable'],
|
||||
];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of additional setting names that should be stored with the objects.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _getObjectAdditionalSettings()
|
||||
{
|
||||
return [$this->getDepositStatusSettingName()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc AcronPlugin::parseCronTab()
|
||||
*/
|
||||
public function callbackParseCronTab($hookName, $args)
|
||||
{
|
||||
$taskFilesPath = & $args[0];
|
||||
|
||||
$scheduledTasksPath = "{$this->getPluginPath()}/scheduledTasks.xml";
|
||||
|
||||
if (!file_exists($scheduledTasksPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$taskFilesPath[] = $scheduledTasksPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all unregistered articles.
|
||||
*
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUnregisteredArticles($context)
|
||||
{
|
||||
// Retrieve all published submissions that have not yet been registered.
|
||||
$articles = Repo::submission()->dao->getExportable(
|
||||
$context->getId(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
$this->getDepositStatusSettingName(),
|
||||
EXPORT_STATUS_NOT_DEPOSITED,
|
||||
null
|
||||
);
|
||||
return $articles->toArray();
|
||||
}
|
||||
/**
|
||||
* Check whether we are in test mode.
|
||||
*
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTestMode($context)
|
||||
{
|
||||
return ($this->getSetting($context->getId(), 'testMode') == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deposit status setting name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDepositStatusSettingName()
|
||||
{
|
||||
return $this->getPluginSettingsPrefix() . '::status';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @copydoc PKPImportExportPlugin::usage
|
||||
*/
|
||||
public function usage($scriptName)
|
||||
{
|
||||
echo __(
|
||||
'plugins.importexport.' . $this->getPluginSettingsPrefix() . '.cliUsage',
|
||||
[
|
||||
'scriptName' => $scriptName,
|
||||
'pluginName' => $this->getName()
|
||||
]
|
||||
) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPImportExportPlugin::executeCLI()
|
||||
*/
|
||||
public function executeCLI($scriptName, &$args)
|
||||
{
|
||||
$command = array_shift($args);
|
||||
if (!in_array($command, ['export', 'register'])) {
|
||||
$this->usage($scriptName);
|
||||
return;
|
||||
}
|
||||
|
||||
$outputFile = $command == 'export' ? array_shift($args) : null;
|
||||
$contextPath = array_shift($args);
|
||||
$objectType = array_shift($args);
|
||||
|
||||
$contextDao = DAORegistry::getDAO('JournalDAO'); /** @var JournalDAO $contextDao */
|
||||
$context = $contextDao->getByPath($contextPath);
|
||||
if (!$context) {
|
||||
if ($contextPath != '') {
|
||||
echo __('plugins.importexport.common.cliError') . "\n";
|
||||
echo __('plugins.importexport.common.error.unknownContext', ['contextPath' => $contextPath]) . "\n\n";
|
||||
}
|
||||
$this->usage($scriptName);
|
||||
return;
|
||||
}
|
||||
|
||||
PluginRegistry::loadCategory('pubIds', true, $context->getId());
|
||||
|
||||
if ($outputFile) {
|
||||
if ($this->isRelativePath($outputFile)) {
|
||||
$outputFile = PWD . '/' . $outputFile;
|
||||
}
|
||||
$outputDir = dirname($outputFile);
|
||||
if (!is_writable($outputDir) || (file_exists($outputFile) && !is_writable($outputFile))) {
|
||||
echo __('plugins.importexport.common.cliError') . "\n";
|
||||
echo __('plugins.importexport.common.export.error.outputFileNotWritable', ['param' => $outputFile]) . "\n\n";
|
||||
$this->usage($scriptName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($objectType) {
|
||||
case 'articles':
|
||||
$objects = $this->getPublishedSubmissions($args, $context);
|
||||
$filter = $this->getSubmissionFilter();
|
||||
$objectsFileNamePart = 'articles';
|
||||
break;
|
||||
case 'issues':
|
||||
$objects = $this->getPublishedIssues($args, $context);
|
||||
$filter = $this->getIssueFilter();
|
||||
$objectsFileNamePart = 'issues';
|
||||
break;
|
||||
case 'galleys':
|
||||
$objects = $this->getArticleGalleys($args, $context);
|
||||
$filter = $this->getRepresentationFilter();
|
||||
$objectsFileNamePart = 'galleys';
|
||||
break;
|
||||
default:
|
||||
$this->usage($scriptName);
|
||||
return;
|
||||
}
|
||||
if (empty($objects)) {
|
||||
echo __('plugins.importexport.common.cliError') . "\n";
|
||||
echo __('plugins.importexport.common.error.unknownObjects') . "\n\n";
|
||||
$this->usage($scriptName);
|
||||
return;
|
||||
}
|
||||
if (!$filter) {
|
||||
$this->usage($scriptName);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->executeCLICommand($scriptName, $command, $context, $outputFile, $objects, $filter, $objectsFileNamePart);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the CLI command
|
||||
*
|
||||
* @param string $scriptName The name of the command-line script (displayed as usage info)
|
||||
* @param string $command (export or register)
|
||||
* @param Journal $context
|
||||
* @param string $outputFile Path to the file where the exported XML should be saved
|
||||
* @param array $objects Objects to be exported or registered
|
||||
* @param string $filter Filter to use
|
||||
* @param string $objectsFileNamePart Export file name part for this kind of objects
|
||||
*/
|
||||
public function executeCLICommand($scriptName, $command, $context, $outputFile, $objects, $filter, $objectsFileNamePart)
|
||||
{
|
||||
$exportXml = $this->exportXML($objects, $filter, $context);
|
||||
if ($command == 'export' && $outputFile) {
|
||||
file_put_contents($outputFile, $exportXml);
|
||||
}
|
||||
|
||||
if ($command == 'register') {
|
||||
$fileManager = new FileManager();
|
||||
$exportFileName = $this->getExportFileName($this->getExportPath(), $objectsFileNamePart, $context, '.xml');
|
||||
$fileManager->writeFile($exportFileName, $exportXml);
|
||||
$result = $this->depositXML($objects, $context, $exportFileName);
|
||||
if ($result === true) {
|
||||
echo __('plugins.importexport.common.register.success') . "\n";
|
||||
} else {
|
||||
echo __('plugins.importexport.common.cliError') . "\n";
|
||||
if (is_array($result)) {
|
||||
foreach ($result as $error) {
|
||||
assert(is_array($error) && count($error) >= 1);
|
||||
$errorMessage = __($error[0], ['param' => ($error[1] ?? null)]);
|
||||
echo "*** {$errorMessage}\n";
|
||||
}
|
||||
echo "\n";
|
||||
} else {
|
||||
echo __('plugins.importexport.common.register.error.mdsError', ['param' => ' - ']) . "\n\n";
|
||||
}
|
||||
$this->usage($scriptName);
|
||||
}
|
||||
$fileManager->deleteByPath($exportFileName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get published submissions from submission IDs.
|
||||
*
|
||||
* @param array $submissionIds
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPublishedSubmissions($submissionIds, $context)
|
||||
{
|
||||
$allSubmissionIds = Repo::submission()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByStatus([PKPSubmission::STATUS_PUBLISHED])
|
||||
->getIds()
|
||||
->toArray();
|
||||
$validSubmissionIds = array_intersect($allSubmissionIds, $submissionIds);
|
||||
return array_map(function ($submissionId) {
|
||||
return Repo::submission()->get($submissionId);
|
||||
}, $validSubmissionIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get published issues from issue IDs.
|
||||
*
|
||||
* @param array $issueIds
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPublishedIssues($issueIds, $context)
|
||||
{
|
||||
return Repo::issue()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->filterByIssueIds($issueIds)
|
||||
->filterByPublished(true)
|
||||
->getMany()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get article galleys from gallley IDs.
|
||||
*
|
||||
* @param array $galleyIds
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getArticleGalleys($galleyIds, $context)
|
||||
{
|
||||
$allGalleyIds = Repo::galley()
|
||||
->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getIds()
|
||||
->toArray();
|
||||
$validGalleyIds = array_intersect($allGalleyIds, $galleyIds);
|
||||
return array_map(function ($galleyId) {
|
||||
return Repo::submission()->get($galleyId);
|
||||
}, $validGalleyIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a notification.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $message An i18n key.
|
||||
* @param int $notificationType One of the NOTIFICATION_TYPE_* constants.
|
||||
* @param string $param An additional parameter for the message.
|
||||
*/
|
||||
public function _sendNotification($user, $message, $notificationType, $param = null)
|
||||
{
|
||||
static $notificationManager = null;
|
||||
$notificationManager ??= new NotificationManager();
|
||||
$params = is_null($param) ? [] : ['param' => $param];
|
||||
$notificationManager->createTrivialNotification(
|
||||
$user->getId(),
|
||||
$notificationType,
|
||||
['contents' => __($message, $params)]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the export deployment.
|
||||
*
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return PKPImportExportDeployment
|
||||
*/
|
||||
public function _instantiateExportDeployment($context)
|
||||
{
|
||||
$exportDeploymentClassName = $this->getExportDeploymentClassName();
|
||||
$exportDeployment = new $exportDeploymentClassName($context, $this);
|
||||
return $exportDeployment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the settings form.
|
||||
*
|
||||
* @param Journal $context
|
||||
*
|
||||
* @return \PKP\form\Form
|
||||
*/
|
||||
public function _instantiateSettingsForm($context)
|
||||
{
|
||||
$settingsFormClassName = $this->getSettingsFormClassName();
|
||||
return new $settingsFormClassName($this, $context->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DAOs for objects that need to be augmented with additional settings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function _getDAOs()
|
||||
{
|
||||
return [
|
||||
Repo::publication()->dao,
|
||||
Repo::submission()->dao,
|
||||
Application::getRepresentationDAO(),
|
||||
Repo::issue()->dao,
|
||||
Repo::submissionFile()->dao
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for export action type as set user var and as action passed from API call
|
||||
*
|
||||
* @param string $exportAction Action to check for
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _checkForExportAction($exportAction)
|
||||
{
|
||||
$request = $this->getRequest();
|
||||
if ($request->getUserVar($exportAction)) {
|
||||
return true;
|
||||
} elseif ($request->getUserVar('action') == $exportAction) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\APP\plugins\PubObjectsExportPlugin', '\PubObjectsExportPlugin');
|
||||
}
|
||||
Reference in New Issue
Block a user