first commit
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/PKPNativeImportExportCLIDeployment.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 PKPNativeImportExportCLIDeployment
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief CLI Deployment for Import/Export operations
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native;
|
||||
|
||||
class PKPNativeImportExportCLIDeployment
|
||||
{
|
||||
/** @var string The import/export script name */
|
||||
private $scriptName;
|
||||
|
||||
/** @var array The import/export arguments */
|
||||
public $args;
|
||||
|
||||
/** @var array The import/export additional directives */
|
||||
public $opts;
|
||||
|
||||
/** @var string The import/export command */
|
||||
public $command;
|
||||
|
||||
/** @var string The import/export xml file name */
|
||||
public $xmlFile;
|
||||
|
||||
/** @var string The import/export operation context path */
|
||||
public $contextPath;
|
||||
|
||||
/** @var string The import/export operation user name */
|
||||
public $userName;
|
||||
|
||||
/** @var string The export entity */
|
||||
public $exportEntity;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($scriptName, $args)
|
||||
{
|
||||
$this->scriptName = $scriptName;
|
||||
$this->args = $args;
|
||||
|
||||
$this->parseCLI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse CLI Command to populate the Deployment's variables
|
||||
*/
|
||||
public function parseCLI()
|
||||
{
|
||||
$this->opts = $this->parseOpts($this->args, ['no-embed', 'use-file-urls']);
|
||||
$this->command = array_shift($this->args);
|
||||
$this->xmlFile = array_shift($this->args);
|
||||
$this->contextPath = array_shift($this->args);
|
||||
|
||||
switch ($this->command) {
|
||||
case 'import':
|
||||
$this->userName = array_shift($this->args);
|
||||
break;
|
||||
case 'export':
|
||||
$this->exportEntity = array_shift($this->args);
|
||||
break;
|
||||
case 'usage':
|
||||
break;
|
||||
default:
|
||||
throw new \BadMethodCallException(__('plugins.importexport.common.error.unknownCommand', ['command' => $this->command]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull out getopt style long options.
|
||||
*
|
||||
* @param array $args
|
||||
* @param array $optCodes
|
||||
*/
|
||||
public function parseOpts(&$args, $optCodes)
|
||||
{
|
||||
$newArgs = [];
|
||||
$opts = [];
|
||||
$sticky = null;
|
||||
foreach ($args as $arg) {
|
||||
if ($sticky) {
|
||||
$opts[$sticky] = $arg;
|
||||
$sticky = null;
|
||||
continue;
|
||||
}
|
||||
if (substr($arg, 0, 2) != '--') {
|
||||
$newArgs[] = $arg;
|
||||
continue;
|
||||
}
|
||||
$opt = substr($arg, 2);
|
||||
if (in_array($opt, $optCodes)) {
|
||||
$opts[$opt] = true;
|
||||
continue;
|
||||
}
|
||||
if (in_array($opt . ':', $optCodes)) {
|
||||
$sticky = $opt;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$args = $newArgs;
|
||||
return $opts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/PKPNativeImportExportCLIToolKit.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 PKPNativeImportExportCLIToolKit
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief CLI Toolkit class
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native;
|
||||
|
||||
use Colors\Color;
|
||||
|
||||
class PKPNativeImportExportCLIToolKit
|
||||
{
|
||||
/**
|
||||
* Echo a CLI Error Message
|
||||
*
|
||||
* @param string $errorMessage
|
||||
*/
|
||||
public function echoCLIError($errorMessage)
|
||||
{
|
||||
$c = new Color();
|
||||
echo $c(__('plugins.importexport.common.cliError'))->white()->bold()->highlight('red') . PHP_EOL;
|
||||
echo $c($errorMessage)->red()->bold() . PHP_EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo export results
|
||||
*
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
* @param string $xmlFile
|
||||
*/
|
||||
public function getCLIExportResult($deployment, $xmlFile)
|
||||
{
|
||||
$c = new Color();
|
||||
$result = $deployment->processResult;
|
||||
$foundErrors = $deployment->isProcessFailed();
|
||||
|
||||
if (!$foundErrors) {
|
||||
$xml = $result->saveXml();
|
||||
file_put_contents($xmlFile, $xml);
|
||||
echo $c(__('plugins.importexport.native.export.completed'))->green()->bold() . PHP_EOL . PHP_EOL;
|
||||
} else {
|
||||
echo $c(__('plugins.importexport.native.processFailed'))->red()->bold() . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo import results
|
||||
*
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
*/
|
||||
public function getCLIImportResult($deployment)
|
||||
{
|
||||
$c = new Color();
|
||||
$result = $deployment->processResult;
|
||||
$foundErrors = $deployment->isProcessFailed();
|
||||
$importedRootObjects = $deployment->getImportedRootEntitiesWithNames();
|
||||
|
||||
if (!$foundErrors) {
|
||||
echo $c(__('plugins.importexport.native.importComplete'))->green()->bold() . PHP_EOL . PHP_EOL;
|
||||
|
||||
foreach ($importedRootObjects as $contentItemName => $contentItemArrays) {
|
||||
echo $c($contentItemName)->white()->bold()->highlight('black') . PHP_EOL;
|
||||
foreach ($contentItemArrays as $contentItemArray) {
|
||||
foreach ($contentItemArray as $contentItem) {
|
||||
echo $c('-' . $contentItem->getUIDisplayString())->white()->bold() . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo $c(__('plugins.importexport.native.processFailed'))->red()->bold() . PHP_EOL . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo import/export possible warnings and errors
|
||||
*
|
||||
* @param PKPNativeImportExportDeployment $deployment
|
||||
*/
|
||||
public function getCLIProblems($deployment)
|
||||
{
|
||||
$result = $deployment->processResult;
|
||||
$problems = $deployment->getWarningsAndErrors();
|
||||
$foundErrors = $deployment->isProcessFailed();
|
||||
|
||||
$warnings = [];
|
||||
if (array_key_exists('warnings', $problems)) {
|
||||
$warnings = $problems['warnings'];
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
if (array_key_exists('errors', $problems)) {
|
||||
$errors = $problems['errors'];
|
||||
}
|
||||
|
||||
// Are there any import warnings? Display them.
|
||||
$this->displayCLIIssues($warnings, __('plugins.importexport.common.warningsEncountered'));
|
||||
$this->displayCLIIssues($errors, __('plugins.importexport.common.errorsOccured'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Echo import/export possible warnings and errors
|
||||
*
|
||||
* @param array $relatedIssues
|
||||
* @param string $title
|
||||
*/
|
||||
public function displayCLIIssues($relatedIssues, $title)
|
||||
{
|
||||
$c = new Color();
|
||||
|
||||
if (count($relatedIssues) > 0) {
|
||||
echo $c($title)->black()->bold()->highlight('light_gray') . PHP_EOL;
|
||||
$i = 0;
|
||||
foreach ($relatedIssues as $relatedTypeName => $allRelatedTypes) {
|
||||
foreach ($allRelatedTypes as $thisTypeId => $thisTypeIds) {
|
||||
if (count($thisTypeIds) > 0) {
|
||||
echo ++$i . '.' . $relatedTypeName . PHP_EOL;
|
||||
foreach ($thisTypeIds as $idRelatedItems) {
|
||||
foreach ($idRelatedItems as $relatedItemMessage) {
|
||||
echo '- ' . $relatedItemMessage . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/PKPNativeImportExportDeployment.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 PKPNativeImportExportDeployment
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class configuring the native import/export process to an
|
||||
* application's specifics.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native;
|
||||
|
||||
use PKP\plugins\importexport\PKPImportExportDeployment;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
class PKPNativeImportExportDeployment extends PKPImportExportDeployment
|
||||
{
|
||||
//
|
||||
// Deployment items for subclasses to override
|
||||
//
|
||||
/**
|
||||
* Get the submission node name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissionNodeName()
|
||||
{
|
||||
return 'submission';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submissions node name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSubmissionsNodeName()
|
||||
{
|
||||
return 'submissions';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the namespace URN
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return 'http://pkp.sfu.ca';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the schema filename.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSchemaFilename()
|
||||
{
|
||||
return 'pkp-native.xsd';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping between stage names in XML and their numeric consts
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStageNameStageIdMapping()
|
||||
{
|
||||
return [
|
||||
'submission' => SubmissionFile::SUBMISSION_FILE_SUBMISSION,
|
||||
'note' => SubmissionFile::SUBMISSION_FILE_NOTE,
|
||||
'review_file' => SubmissionFile::SUBMISSION_FILE_REVIEW_FILE,
|
||||
'review_attachment' => SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT,
|
||||
'final' => SubmissionFile::SUBMISSION_FILE_FINAL,
|
||||
'copyedit' => SubmissionFile::SUBMISSION_FILE_COPYEDIT,
|
||||
'proof' => SubmissionFile::SUBMISSION_FILE_PROOF,
|
||||
'production_ready' => SubmissionFile::SUBMISSION_FILE_PRODUCTION_READY,
|
||||
'attachment' => SubmissionFile::SUBMISSION_FILE_ATTACHMENT,
|
||||
'review_revision' => SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION,
|
||||
'dependent' => SubmissionFile::SUBMISSION_FILE_DEPENDENT,
|
||||
'query' => SubmissionFile::SUBMISSION_FILE_QUERY,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/PKPNativeImportExportPlugin.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 PKPNativeImportExportPlugin
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Native XML import/export plugin
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\template\TemplateManager;
|
||||
use BadMethodCallException;
|
||||
use Exception;
|
||||
use PKP\core\JSONMessage;
|
||||
use PKP\file\TemporaryFileManager;
|
||||
use PKP\plugins\ImportExportPlugin;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
|
||||
abstract class PKPNativeImportExportPlugin extends ImportExportPlugin
|
||||
{
|
||||
/** @var PKPNativeImportExportCLIDeployment CLI Deployment for import/export operations */
|
||||
protected $cliDeployment = null;
|
||||
|
||||
/** @var string Display operation result */
|
||||
protected $result = null;
|
||||
|
||||
/** @var bool Indication that the parent code has managed the display operation */
|
||||
protected $isResultManaged = false;
|
||||
|
||||
/** @var PKPNativeImportExportCLIToolKit The helper for CLI import/export operations */
|
||||
protected $cliToolkit;
|
||||
|
||||
/** @var string Operation type for display method */
|
||||
protected $opType;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->cliToolkit = new PKPNativeImportExportCLIToolKit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc Plugin::register()
|
||||
*
|
||||
* @param null|mixed $mainContextId
|
||||
*/
|
||||
public function register($category, $path, $mainContextId = null)
|
||||
{
|
||||
$success = parent::register($category, $path, $mainContextId);
|
||||
if (Application::isUnderMaintenance()) {
|
||||
return $success;
|
||||
}
|
||||
if ($success && $this->getEnabled()) {
|
||||
$this->addLocaleData();
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this plugin. The name must be unique within
|
||||
* its category.
|
||||
*
|
||||
* @return string name of plugin
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'NativeImportExportPlugin';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName()
|
||||
{
|
||||
return __('plugins.importexport.native.displayName');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
return __('plugins.importexport.native.description');
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc ImportExportPlugin::getPluginSettingsPrefix()
|
||||
*/
|
||||
public function getPluginSettingsPrefix()
|
||||
{
|
||||
return 'native';
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ImportExportPlugin::display()
|
||||
*/
|
||||
public function display($args, $request)
|
||||
{
|
||||
$templateMgr = TemplateManager::getManager($request);
|
||||
parent::display($args, $request);
|
||||
|
||||
$context = $request->getContext();
|
||||
$user = $request->getUser();
|
||||
$deployment = $this->getAppSpecificDeployment($context, $user);
|
||||
$this->setDeployment($deployment);
|
||||
|
||||
$this->opType = array_shift($args);
|
||||
switch ($this->opType) {
|
||||
case 'index':
|
||||
case '':
|
||||
$apiUrl = $request->getDispatcher()->url($request, Application::ROUTE_API, $context->getPath(), 'submissions');
|
||||
$submissionsListPanel = new \APP\components\listPanels\SubmissionsListPanel(
|
||||
'submissions',
|
||||
__('common.publications'),
|
||||
[
|
||||
'apiUrl' => $apiUrl,
|
||||
'count' => 100,
|
||||
'getParams' => new \stdClass(),
|
||||
'lazyLoad' => true,
|
||||
]
|
||||
);
|
||||
$submissionsConfig = $submissionsListPanel->getConfig();
|
||||
$submissionsConfig['addUrl'] = '';
|
||||
$submissionsConfig['filters'] = array_slice($submissionsConfig['filters'], 1);
|
||||
$templateMgr->setState([
|
||||
'components' => [
|
||||
'submissions' => $submissionsConfig,
|
||||
],
|
||||
]);
|
||||
$templateMgr->assign([
|
||||
'pageComponent' => 'ImportExportPage',
|
||||
]);
|
||||
|
||||
$templateMgr->display($this->getTemplateResource('index.tpl'));
|
||||
|
||||
$this->isResultManaged = true;
|
||||
break;
|
||||
case 'uploadImportXML':
|
||||
$temporaryFileManager = new TemporaryFileManager();
|
||||
$temporaryFile = $temporaryFileManager->handleUpload('uploadedFile', $user->getId());
|
||||
if ($temporaryFile) {
|
||||
$json = new JSONMessage(true);
|
||||
$json->setAdditionalAttributes([
|
||||
'temporaryFileId' => $temporaryFile->getId()
|
||||
]);
|
||||
} else {
|
||||
$json = new JSONMessage(false, __('common.uploadFailed'));
|
||||
}
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$this->result = $json->getString();
|
||||
$this->isResultManaged = true;
|
||||
|
||||
break;
|
||||
case 'importBounce':
|
||||
$tempFileId = $request->getUserVar('temporaryFileId');
|
||||
|
||||
if (empty($tempFileId)) {
|
||||
$this->result = new JSONMessage(false);
|
||||
$this->isResultManaged = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$tab = $this->getBounceTab(
|
||||
$request,
|
||||
__('plugins.importexport.native.results'),
|
||||
'import',
|
||||
['temporaryFileId' => $tempFileId]
|
||||
);
|
||||
|
||||
$this->result = $tab;
|
||||
$this->isResultManaged = true;
|
||||
break;
|
||||
case 'exportSubmissionsBounce':
|
||||
$tab = $this->getBounceTab(
|
||||
$request,
|
||||
__('plugins.importexport.native.export.submissions.results'),
|
||||
'exportSubmissions',
|
||||
['selectedSubmissions' => $request->getUserVar('selectedSubmissions')]
|
||||
);
|
||||
|
||||
$this->result = $tab;
|
||||
$this->isResultManaged = true;
|
||||
|
||||
break;
|
||||
case 'import':
|
||||
if (!$request->checkCSRF()) {
|
||||
throw new Exception('CSRF mismatch!');
|
||||
}
|
||||
$temporaryFilePath = $this->getImportedFilePath($request->getUserVar('temporaryFileId'), $user);
|
||||
[$filter, $xmlString] = $this->getImportFilter($temporaryFilePath);
|
||||
$result = $this->getImportTemplateResult($filter, $xmlString, $this->getDeployment(), $templateMgr);
|
||||
|
||||
$this->result = $result;
|
||||
$this->isResultManaged = true;
|
||||
|
||||
break;
|
||||
case 'exportSubmissions':
|
||||
$submissionIds = (array) $request->getUserVar('selectedSubmissions');
|
||||
|
||||
$this->getExportSubmissionsDeployment($submissionIds, $this->_childDeployment);
|
||||
|
||||
$result = $this->getExportTemplateResult($this->getDeployment(), $templateMgr, 'submissions');
|
||||
|
||||
$this->result = $result;
|
||||
$this->isResultManaged = true;
|
||||
|
||||
break;
|
||||
case 'downloadExportFile':
|
||||
$exportedFileDatePart = $request->getUserVar('exportedFileDatePart');
|
||||
$exportedFileContentNamePart = $request->getUserVar('exportedFileContentNamePart');
|
||||
$downloadSuccess = $this->downloadExportedFile($exportedFileContentNamePart, $exportedFileDatePart, $this->getDeployment());
|
||||
|
||||
if (!$downloadSuccess) {
|
||||
$dispatcher = $request->getDispatcher();
|
||||
$dispatcher->handle404();
|
||||
}
|
||||
|
||||
$this->isResultManaged = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the XML for a set of submissions.
|
||||
*
|
||||
* @param array $submissionIds Array of submission IDs
|
||||
* @param \PKP\context\Context $context
|
||||
* @param \PKP\user\User|null $user
|
||||
* @param array $opts
|
||||
*
|
||||
* @return string XML contents representing the supplied submission IDs.
|
||||
*/
|
||||
public function exportSubmissions($submissionIds, $context, $user, $opts = [])
|
||||
{
|
||||
$appSpecificDeployment = $this->getAppSpecificDeployment($context, null);
|
||||
$this->setDeployment($appSpecificDeployment);
|
||||
|
||||
$this->getExportSubmissionsDeployment($submissionIds, $appSpecificDeployment, $opts);
|
||||
|
||||
return $this->exportResultXML($appSpecificDeployment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc PKPImportExportPlugin::usage
|
||||
*/
|
||||
public function usage($scriptName)
|
||||
{
|
||||
echo __('plugins.importexport.native.cliUsage', [
|
||||
'scriptName' => $scriptName,
|
||||
'pluginName' => $this->getName()
|
||||
]) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PKPImportExportPlugin::executeCLI()
|
||||
*/
|
||||
public function executeCLI($scriptName, &$args)
|
||||
{
|
||||
try {
|
||||
$cliDeployment = new PKPNativeImportExportCLIDeployment($scriptName, $args);
|
||||
} catch (BadMethodCallException $ex) {
|
||||
$this->cliToolkit->echoCLIError($ex->getMessage());
|
||||
$this->usage($scriptName);
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->cliDeployment = $cliDeployment;
|
||||
|
||||
$contextDao = Application::getContextDAO();
|
||||
|
||||
$contextPath = $cliDeployment->contextPath;
|
||||
$context = $contextDao->getByPath($contextPath);
|
||||
|
||||
if (!$context) {
|
||||
if ($contextPath != '') {
|
||||
$this->cliToolkit->echoCLIError(__('plugins.importexport.common.error.unknownContext', ['contextPath' => $contextPath]));
|
||||
}
|
||||
$this->usage($scriptName);
|
||||
return true;
|
||||
}
|
||||
|
||||
PluginRegistry::loadCategory('pubIds', true, $context->getId());
|
||||
|
||||
$xmlFile = $cliDeployment->xmlFile;
|
||||
if ($xmlFile && $this->isRelativePath($xmlFile)) {
|
||||
$xmlFile = PWD . '/' . $xmlFile;
|
||||
}
|
||||
|
||||
$appSpecificDeployment = $this->getAppSpecificDeployment($context, null);
|
||||
$this->setDeployment($appSpecificDeployment);
|
||||
|
||||
switch ($cliDeployment->command) {
|
||||
case 'import':
|
||||
$user = Application::get()->getRequest()->getUser();
|
||||
|
||||
if (!$user) {
|
||||
$this->cliToolkit->echoCLIError(__('plugins.importexport.native.error.unknownUser'));
|
||||
$this->usage($scriptName);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!file_exists($xmlFile)) {
|
||||
$this->cliToolkit->echoCLIError(__('plugins.importexport.common.export.error.inputFileNotReadable', ['param' => $xmlFile]));
|
||||
|
||||
$this->usage($scriptName);
|
||||
return true;
|
||||
}
|
||||
|
||||
[$filter, $xmlString] = $this->getImportFilter($xmlFile);
|
||||
|
||||
$deployment = $this->getDeployment(); /** @var PKPNativeImportExportDeployment $deployment */
|
||||
$deployment->setUser($user);
|
||||
$deployment->setImportPath(dirname($xmlFile));
|
||||
|
||||
$deployment->import($filter, $xmlString);
|
||||
|
||||
$this->cliToolkit->getCLIImportResult($deployment);
|
||||
$this->cliToolkit->getCLIProblems($deployment);
|
||||
return true;
|
||||
case 'export':
|
||||
$deployment = $this->getDeployment(); /** @var PKPNativeImportExportDeployment $deployment */
|
||||
|
||||
$outputDir = dirname($xmlFile);
|
||||
if (!is_writable($outputDir) || (file_exists($xmlFile) && !is_writable($xmlFile))) {
|
||||
$this->cliToolkit->echoCLIError(__('plugins.importexport.common.export.error.outputFileNotWritable', ['param' => $xmlFile]));
|
||||
|
||||
$this->usage($scriptName);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($cliDeployment->xmlFile != '') {
|
||||
switch ($cliDeployment->exportEntity) {
|
||||
case $deployment->getSubmissionNodeName():
|
||||
case $deployment->getSubmissionsNodeName():
|
||||
$this->getExportSubmissionsDeployment(
|
||||
$cliDeployment->args,
|
||||
$deployment,
|
||||
$cliDeployment->opts
|
||||
);
|
||||
|
||||
$this->cliToolkit->getCLIExportResult($deployment, $xmlFile);
|
||||
$this->cliToolkit->getCLIProblems($deployment);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
$this->usage($scriptName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeExportFilter.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 NativeExportFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a DataObject to a Native XML document
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use PKP\plugins\importexport\PKPImportExportFilter;
|
||||
use PKP\xslt\XMLTypeDescription;
|
||||
|
||||
class NativeExportFilter extends PKPImportExportFilter
|
||||
{
|
||||
/** @var bool If set to true no validation (e.g. XML validation) will be done */
|
||||
public $_noValidation = null;
|
||||
public $opts = [];
|
||||
|
||||
/**
|
||||
* Set no validation option
|
||||
*
|
||||
* @param bool $noValidation
|
||||
*/
|
||||
public function setNoValidation($noValidation)
|
||||
{
|
||||
$this->_noValidation = $noValidation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get no validation option
|
||||
*
|
||||
* @return bool true|null
|
||||
*/
|
||||
public function getNoValidation()
|
||||
{
|
||||
return $this->_noValidation;
|
||||
}
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
/**
|
||||
* @copydoc Filter::supports()
|
||||
*/
|
||||
public function supports(&$input, &$output)
|
||||
{
|
||||
// Validate input
|
||||
$inputType = & $this->getInputType();
|
||||
$validInput = $inputType->isCompatible($input);
|
||||
|
||||
// If output is null then we're done
|
||||
if (is_null($output)) {
|
||||
return $validInput;
|
||||
}
|
||||
|
||||
// Validate output
|
||||
$outputType = & $this->getOutputType();
|
||||
|
||||
if ($outputType instanceof XMLTypeDescription && $this->getNoValidation()) {
|
||||
$outputType->setValidationStrategy(XMLTypeDescription::XML_TYPE_DESCRIPTION_VALIDATE_NONE);
|
||||
}
|
||||
$validOutput = $outputType->isCompatible($output);
|
||||
|
||||
return $validInput && $validOutput;
|
||||
}
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
/**
|
||||
* Create a set of child nodes of parentNode containing the
|
||||
* localeKey => value data representing translated content.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMNode $parentNode
|
||||
* @param string $name Node name
|
||||
* @param array $values Array of locale key => value mappings
|
||||
*/
|
||||
public function createLocalizedNodes($doc, $parentNode, $name, $values)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
foreach (is_array($values) ? $values : [] as $locale => $value) {
|
||||
if ($value === '') { // Skip empty values
|
||||
continue;
|
||||
}
|
||||
|
||||
$node = $doc->createElementNS($deployment->getNamespace(), $name, htmlspecialchars($value, ENT_COMPAT));
|
||||
$node->setAttribute('locale', $locale);
|
||||
$parentNode->appendChild($node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an optional node with a name and value.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $parentNode
|
||||
* @param string $name
|
||||
* @param string|null $value
|
||||
*
|
||||
* @return ?\DOMElement
|
||||
*/
|
||||
public function createOptionalNode($doc, $parentNode, $name, $value)
|
||||
{
|
||||
if ($value === '' || $value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$deployment = $this->getDeployment();
|
||||
$parentNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), $name, htmlspecialchars($value, ENT_COMPAT, 'UTF-8')));
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set xml filtering opts
|
||||
*
|
||||
* @param array $opts
|
||||
*/
|
||||
public function setOpts($opts)
|
||||
{
|
||||
$this->opts = $opts;
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\importexport\native\filter\NativeExportFilter', '\NativeExportFilter');
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeImportFilter.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 NativeImportFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Native XML document to a DataObject
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use Exception;
|
||||
use PKP\plugins\importexport\PKPImportExportFilter;
|
||||
|
||||
class NativeImportFilter extends PKPImportExportFilter
|
||||
{
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param \DOMDocument|string $document
|
||||
*
|
||||
* @return array Array of imported documents
|
||||
*/
|
||||
public function &process(&$document)
|
||||
{
|
||||
// If necessary, convert $document to a DOMDocument.
|
||||
if (is_string($document)) {
|
||||
$xmlString = $document;
|
||||
$document = new \DOMDocument('1.0', 'utf-8');
|
||||
$document->loadXml($xmlString);
|
||||
}
|
||||
assert($document instanceof \DOMDocument);
|
||||
|
||||
$importedObjects = [];
|
||||
if ($document->documentElement->tagName == $this->getPluralElementName()) {
|
||||
// Multiple element (plural) import
|
||||
for ($n = $document->documentElement->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if (!($n instanceof \DOMElement)) {
|
||||
continue;
|
||||
}
|
||||
$object = $this->handleElement($n);
|
||||
if ($object) {
|
||||
$importedObjects[] = $object;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert($document->documentElement->tagName == $this->getSingularElementName());
|
||||
|
||||
// Single element (singular) import
|
||||
$object = $this->handleElement($document->documentElement);
|
||||
if ($object) {
|
||||
$importedObjects[] = $object;
|
||||
}
|
||||
}
|
||||
|
||||
return $importedObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the plural element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluralElementName()
|
||||
{
|
||||
assert(false); // Must be overridden by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singular element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSingularElementName()
|
||||
{
|
||||
assert(false); // Must be overridden by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a singular element import
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
* @return object
|
||||
*/
|
||||
public function handleElement($node)
|
||||
{
|
||||
assert(false); // Must be overridden by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a localized element
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
*
|
||||
* @return array Array("locale_KEY", "Localized Text")
|
||||
*/
|
||||
public function parseLocalizedContent($element)
|
||||
{
|
||||
return [$element->getAttribute('locale'), $element->textContent];
|
||||
}
|
||||
|
||||
/**
|
||||
* Import node to a given parent node
|
||||
*
|
||||
* @param \DOMElement $n The parent node
|
||||
* @param string $filter The filter to execute it's import function
|
||||
*/
|
||||
public function importWithXMLNode($n, $filter = null)
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'utf-8');
|
||||
$doc->appendChild($doc->importNode($n, true));
|
||||
$importFilter = null;
|
||||
if ($filter) {
|
||||
$importFilter = PKPImportExportFilter::getFilter($filter, $this->getDeployment());
|
||||
} elseif (method_exists($this, 'getImportFilter')) {
|
||||
$importFilter = $this->getImportFilter($n->tagName);
|
||||
} else {
|
||||
throw new Exception(__('filter.import.error.couldNotImportNode'));
|
||||
}
|
||||
|
||||
return $importFilter->execute($doc);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PKP_STRICT_MODE) {
|
||||
class_alias('\PKP\plugins\importexport\native\filter\NativeImportFilter', '\NativeImportFilter');
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeXmlPKPAuthorFilter.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 NativeXmlPKPAuthorFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Native XML document to a set of authors
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\publication\Publication;
|
||||
use Exception;
|
||||
use PKP\author\Author;
|
||||
use PKP\facades\Locale;
|
||||
use PKP\filter\FilterGroup;
|
||||
|
||||
class NativeXmlPKPAuthorFilter extends NativeImportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML author import');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from NativeImportFilter
|
||||
//
|
||||
/**
|
||||
* Return the plural element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluralElementName()
|
||||
{
|
||||
return 'authors';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singular element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSingularElementName()
|
||||
{
|
||||
return 'author';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an author element
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
*
|
||||
* @return \PKP\author\Author
|
||||
*/
|
||||
public function handleElement($node)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
|
||||
$publication = $deployment->getPublication();
|
||||
assert($publication instanceof Publication);
|
||||
|
||||
// Create the data object
|
||||
$author = Repo::author()->newDataObject();
|
||||
|
||||
$author->setData('publicationId', $publication->getId());
|
||||
if ($node->getAttribute('primary_contact')) {
|
||||
$author->setPrimaryContact(true);
|
||||
}
|
||||
if ($node->getAttribute('include_in_browse')) {
|
||||
$author->setIncludeInBrowse(true);
|
||||
}
|
||||
if ($node->getAttribute('seq')) {
|
||||
$author->setSequence($node->getAttribute('seq'));
|
||||
}
|
||||
|
||||
// Handle metadata in subelements
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof \DOMElement) {
|
||||
switch ($n->tagName) {
|
||||
case 'givenname':
|
||||
$locale = $n->getAttribute('locale');
|
||||
if (empty($locale)) {
|
||||
$locale = $publication->getData('locale');
|
||||
}
|
||||
$author->setGivenName($n->textContent, $locale);
|
||||
break;
|
||||
case 'familyname':
|
||||
$locale = $n->getAttribute('locale');
|
||||
if (empty($locale)) {
|
||||
$locale = $publication->getData('locale');
|
||||
}
|
||||
$author->setFamilyName($n->textContent, $locale);
|
||||
break;
|
||||
case 'affiliation':
|
||||
$locale = $n->getAttribute('locale');
|
||||
if (empty($locale)) {
|
||||
$locale = $publication->getData('locale');
|
||||
}
|
||||
$author->setAffiliation($n->textContent, $locale);
|
||||
break;
|
||||
case 'country': $author->setCountry($n->textContent);
|
||||
break;
|
||||
case 'email': $author->setEmail($n->textContent);
|
||||
break;
|
||||
case 'url': $author->setUrl($n->textContent);
|
||||
break;
|
||||
case 'orcid': $author->setOrcid($n->textContent);
|
||||
break;
|
||||
case 'biography':
|
||||
$locale = $n->getAttribute('locale');
|
||||
if (empty($locale)) {
|
||||
$locale = $publication->getData('locale');
|
||||
}
|
||||
$author->setBiography($n->textContent, $locale);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$authorGivenName = $author->getFullName(true, false, $publication->getData('locale'));
|
||||
if (empty($authorGivenName)) {
|
||||
$deployment->addError(
|
||||
Application::ASSOC_TYPE_SUBMISSION,
|
||||
$publication->getId(),
|
||||
__('plugins.importexport.common.error.missingGivenName', [
|
||||
'authorName' => $author->getLocalizedGivenName(),
|
||||
'localeName' => Locale::getMetadata($publication->getData('locale'))->getDisplayName()
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// Identify the user group by name
|
||||
$userGroupName = $node->getAttribute('user_group_ref');
|
||||
|
||||
$userGroups = Repo::userGroup()->getCollector()
|
||||
->filterByContextIds([$context->getId()])
|
||||
->getMany();
|
||||
|
||||
foreach ($userGroups as $userGroup) {
|
||||
if (in_array($userGroupName, $userGroup->getName(null))) {
|
||||
// Found a candidate; stash it.
|
||||
$author->setUserGroupId($userGroup->getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$author->getUserGroupId()) {
|
||||
$authorFullName = $author->getFullName(true, false, $publication->getData('locale'));
|
||||
$deployment->addError(Application::ASSOC_TYPE_AUTHOR, $publication->getId(), __('plugins.importexport.common.error.unknownUserGroup', ['authorName' => $authorFullName, 'userGroupName' => $userGroupName]));
|
||||
throw new Exception(__('plugins.importexport.author.exportFailed'));
|
||||
}
|
||||
|
||||
$authorId = Repo::author()->add($author);
|
||||
$author->setId($authorId);
|
||||
|
||||
$importAuthorId = $node->getAttribute('id');
|
||||
$deployment->setAuthorDBId($importAuthorId, $authorId);
|
||||
|
||||
if ($node->getAttribute('id') == $publication->getData('primaryContactId')) {
|
||||
$publication->setData('primaryContactId', $author->getId());
|
||||
}
|
||||
|
||||
return $author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an identifier node
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param \PKP\author\Author $author
|
||||
*/
|
||||
public function parseIdentifier($element, $author)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$publication = $deployment->getPublication();
|
||||
|
||||
$advice = $element->getAttribute('advice');
|
||||
switch ($element->getAttribute('type')) {
|
||||
case 'internal':
|
||||
// "update" advice not supported yet.
|
||||
assert(!$advice || $advice == 'ignore');
|
||||
|
||||
if ($element->textContent == $publication->getData('primaryContactId')) {
|
||||
$publication->setData('primaryContactId', $author->getId());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeXmlPKPPublicationFilter.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 NativeXmlPKPPublicationFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Native XML document to a set of publications
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\publication\Publication;
|
||||
use PKP\citation\CitationDAO;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\filter\Filter;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
|
||||
class NativeXmlPKPPublicationFilter extends NativeImportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML publication import');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from NativeImportFilter
|
||||
//
|
||||
/**
|
||||
* Return the plural element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluralElementName()
|
||||
{
|
||||
return 'publications';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singular element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSingularElementName()
|
||||
{
|
||||
return 'publication';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a singular element import.
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
*/
|
||||
public function handleElement($node)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
$submission = $deployment->getSubmission();
|
||||
|
||||
$publication = Repo::publication()->newDataObject();
|
||||
|
||||
$publication->setData('submissionId', $submission->getId());
|
||||
|
||||
$publication->stampModified();
|
||||
$publication = $this->populateObject($publication, $node);
|
||||
|
||||
$publication->setData('version', $node->getAttribute('version'));
|
||||
$publication->setData('seq', $node->getAttribute('seq'));
|
||||
$publication->setData('accessStatus', $node->getAttribute('access_status'));
|
||||
$publication->setData('status', $node->getAttribute('status'));
|
||||
$publication->setData('urlPath', strlen($urlPath = (string) $node->getAttribute('url_path')) ? $urlPath : null);
|
||||
|
||||
$publicationId = Repo::publication()->dao->insert($publication);
|
||||
$publication = Repo::publication()->get($publicationId);
|
||||
// Non-persisted temporary ID, will be updated and stored once the authors get parsed
|
||||
$publication->setData('primaryContactId', $node->getAttribute('primary_contact_id'));
|
||||
$deployment->setPublication($publication);
|
||||
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof \DOMElement) {
|
||||
$this->handleChildElement($n, $publication);
|
||||
}
|
||||
}
|
||||
|
||||
Repo::publication()->dao->update($publication);
|
||||
|
||||
return Repo::publication()->get($publication->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the entity object from the node
|
||||
*
|
||||
* @param Publication $publication
|
||||
* @param \DOMElement $node
|
||||
*
|
||||
* @return Publication
|
||||
*/
|
||||
public function populateObject($publication, $node)
|
||||
{
|
||||
if ($datePublished = $node->getAttribute('date_published')) {
|
||||
$publication->setData('datePublished', $datePublished);
|
||||
}
|
||||
|
||||
return $publication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an element whose parent is the publication element.
|
||||
*
|
||||
* @param \DOMElement $n
|
||||
* @param Publication $publication
|
||||
*/
|
||||
public function handleChildElement($n, $publication)
|
||||
{
|
||||
$setterMappings = $this->_getLocalizedPublicationFields();
|
||||
$controlledVocabulariesMappings = $this->_getControlledVocabulariesMappings();
|
||||
|
||||
[$locale, $value] = $this->parseLocalizedContent($n);
|
||||
if (empty($locale)) {
|
||||
$locale = $publication->getData('locale');
|
||||
}
|
||||
|
||||
if (in_array($n->tagName, $setterMappings)) {
|
||||
$publication->setData($n->tagName, $value, $locale);
|
||||
} elseif (isset($controlledVocabulariesMappings[$n->tagName])) {
|
||||
$controlledVocabulariesDao = $submissionKeywordDao = DAORegistry::getDAO($controlledVocabulariesMappings[$n->tagName][0]);
|
||||
$insertFunction = $controlledVocabulariesMappings[$n->tagName][1];
|
||||
|
||||
$controlledVocabulary = [];
|
||||
for ($nc = $n->firstChild; $nc !== null; $nc = $nc->nextSibling) {
|
||||
if ($nc instanceof \DOMElement) {
|
||||
$controlledVocabulary[] = $nc->textContent;
|
||||
}
|
||||
}
|
||||
|
||||
$controlledVocabulariesValues = [];
|
||||
$controlledVocabulariesValues[$locale] = $controlledVocabulary;
|
||||
|
||||
$controlledVocabulariesDao->$insertFunction($controlledVocabulariesValues, $publication->getId(), false);
|
||||
|
||||
$publicationNew = Repo::publication()->get($publication->getId());
|
||||
$publication->setData($n->tagName, $publicationNew->getData($n->tagName));
|
||||
} else {
|
||||
switch ($n->tagName) {
|
||||
// Otherwise, delegate to specific parsing code
|
||||
case 'id':
|
||||
$this->parseIdentifier($n, $publication);
|
||||
break;
|
||||
case 'authors':
|
||||
$this->parseAuthors($n, $publication);
|
||||
break;
|
||||
case 'citations':
|
||||
$this->parseCitations($n, $publication);
|
||||
break;
|
||||
case 'copyrightYear':
|
||||
$publication->setData('copyrightYear', $n->textContent);
|
||||
break;
|
||||
case 'licenseUrl':
|
||||
$publication->setData('licenseUrl', $n->textContent);
|
||||
break;
|
||||
default:
|
||||
$deployment = $this->getDeployment();
|
||||
$deployment->addWarning(Application::ASSOC_TYPE_PUBLICATION, $publication->getId(), __('plugins.importexport.common.error.unknownElement', ['param' => $n->tagName]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Element parsing
|
||||
//
|
||||
/**
|
||||
* Parse an identifier node and set up the publication object accordingly
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param Publication $publication
|
||||
*/
|
||||
public function parseIdentifier($element, $publication)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$submission = $deployment->getSubmission();
|
||||
|
||||
$advice = $element->getAttribute('advice');
|
||||
switch ($element->getAttribute('type')) {
|
||||
case 'internal':
|
||||
// "update" advice not supported yet.
|
||||
assert(!$advice || $advice == 'ignore');
|
||||
|
||||
if ($element->textContent == $submission->getData('currentPublicationId')) {
|
||||
$submission->setData('currentPublicationId', $publication->getId());
|
||||
Repo::submission()->dao->update($submission);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'public':
|
||||
if ($advice == 'update') {
|
||||
$publication->setData('pub-id::publisher-id', $element->textContent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($advice == 'update') {
|
||||
if ($element->getAttribute('type') == 'doi') {
|
||||
$doiFound = Repo::doi()->getCollector()->filterByIdentifier($element->textContent)->getMany()->first();
|
||||
if ($doiFound) {
|
||||
$publication->setData('doiId', $doiFound->getId());
|
||||
} else {
|
||||
$newDoiObject = Repo::doi()->newDataObject(
|
||||
[
|
||||
'doi' => $element->textContent,
|
||||
'contextId' => $submission->getData('contextId')
|
||||
]
|
||||
);
|
||||
$doiId = Repo::doi()->add($newDoiObject);
|
||||
$publication->setData('doiId', $doiId);
|
||||
}
|
||||
} else {
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $deployment->getContext()->getId());
|
||||
$publication->setData('pub-id::' . $element->getAttribute('type'), $element->textContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an authors element
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
* @param Publication $publication
|
||||
*/
|
||||
public function parseAuthors($node, $publication)
|
||||
{
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof \DOMElement) {
|
||||
assert($n->tagName == 'author');
|
||||
$this->parseAuthor($n, $publication);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an author and add it to the submission.
|
||||
*
|
||||
* @param \DOMElement $n
|
||||
* @param Publication $publication
|
||||
*/
|
||||
public function parseAuthor($n, $publication)
|
||||
{
|
||||
return $this->importWithXMLNode($n, 'native-xml=>author');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a publication citation and add it to the publication.
|
||||
*
|
||||
* @param \DOMElement $n
|
||||
* @param Publication $publication
|
||||
*/
|
||||
public function parseCitations($n, $publication)
|
||||
{
|
||||
$publicationId = $publication->getId();
|
||||
$citationsString = '';
|
||||
foreach ($n->childNodes as $citNode) {
|
||||
$nodeText = trim($citNode->textContent);
|
||||
if (empty($nodeText)) {
|
||||
continue;
|
||||
}
|
||||
$citationsString .= $nodeText . "\n";
|
||||
}
|
||||
$publication->setData('citationsRaw', $citationsString);
|
||||
$citationDao = DAORegistry::getDAO('CitationDAO'); /** @var CitationDAO $citationDao */
|
||||
$citationDao->importCitations($publicationId, $citationsString);
|
||||
}
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
/**
|
||||
* Get node name to setter function mapping for localized data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function _getLocalizedPublicationFields()
|
||||
{
|
||||
return [
|
||||
'title',
|
||||
'prefix',
|
||||
'subtitle',
|
||||
'abstract',
|
||||
'coverage',
|
||||
'type',
|
||||
'source',
|
||||
'rights',
|
||||
'copyrightHolder',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get node name to DAO and insert function mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function _getControlledVocabulariesMappings()
|
||||
{
|
||||
return [
|
||||
'keywords' => ['SubmissionKeywordDAO', 'insertKeywords'],
|
||||
'agencies' => ['SubmissionAgencyDAO', 'insertAgencies'],
|
||||
'languages' => ['SubmissionLanguageDAO', 'insertLanguages'],
|
||||
'disciplines' => ['SubmissionDisciplineDAO', 'insertDisciplines'],
|
||||
'subjects' => ['SubmissionSubjectDAO', 'insertSubjects'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the representation export filter group name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRepresentationExportFilterGroupName()
|
||||
{
|
||||
assert(false); // Subclasses must override
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import filter for a given element.
|
||||
*
|
||||
* @param string $elementName Name of XML element
|
||||
*
|
||||
* @return Filter
|
||||
*/
|
||||
public function getImportFilter($elementName)
|
||||
{
|
||||
assert(false); // Subclasses should override
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeXmlRepresentationFilter.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 NativeXmlRepresentationFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Native XML document to a set of authors
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\publication\Publication;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\submission\Representation;
|
||||
|
||||
class NativeXmlRepresentationFilter extends NativeImportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML representation import');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a Representation element
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
*
|
||||
* @return Representation
|
||||
*/
|
||||
public function handleElement($node)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
|
||||
$publication = $deployment->getPublication();
|
||||
assert($publication instanceof Publication);
|
||||
|
||||
// Create the data object
|
||||
$representationDao = Application::getRepresentationDAO();
|
||||
$representation = $representationDao->newDataObject(); /** @var Representation $representation */
|
||||
|
||||
$representation->setData('publicationId', $publication->getId());
|
||||
$representation->setData('urlPath', strlen($urlPath = (string) $node->getAttribute('url_path')) ? $urlPath : null);
|
||||
|
||||
// Handle metadata in subelements. Look for the 'name' and 'seq' elements.
|
||||
// All other elements are handled by subclasses.
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof \DOMElement) {
|
||||
switch ($n->tagName) {
|
||||
case 'id': $this->parseIdentifier($n, $representation);
|
||||
break;
|
||||
case 'name':
|
||||
$locale = $n->getAttribute('locale') ?: $publication->getData('locale');
|
||||
$representation->setName($n->textContent, $locale);
|
||||
break;
|
||||
case 'seq':
|
||||
$representation->setSequence($n->textContent);
|
||||
break;
|
||||
case 'remote':
|
||||
$representation->setRemoteURL(($remoteUrl = $n->getAttribute('src')) ? $remoteUrl : null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $representation; // database insert is handled by sub class.
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an identifier node and set up the representation object accordingly
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param Representation $representation
|
||||
*/
|
||||
public function parseIdentifier($element, $representation)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
$advice = $element->getAttribute('advice');
|
||||
switch ($element->getAttribute('type')) {
|
||||
case 'internal':
|
||||
// "update" advice not supported yet.
|
||||
assert(!$advice || $advice == 'ignore');
|
||||
break;
|
||||
case 'public':
|
||||
if ($advice == 'update') {
|
||||
$representation->setStoredPubId('publisher-id', $element->textContent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($advice == 'update') {
|
||||
if ($element->getAttribute('type') == 'doi') {
|
||||
$doiFound = Repo::doi()->getCollector()->filterByIdentifier($element->textContent)->getMany()->first();
|
||||
if ($doiFound) {
|
||||
$representation->setData('doiId', $doiFound->getId());
|
||||
} else {
|
||||
$newDoiObject = Repo::doi()->newDataObject(
|
||||
[
|
||||
'doi' => $element->textContent,
|
||||
'contextId' => $context->getId()
|
||||
]
|
||||
);
|
||||
$doiId = Repo::doi()->add($newDoiObject);
|
||||
$representation->setData('doiId', $doiId);
|
||||
}
|
||||
} else {
|
||||
// Load pub id plugins
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $context->getId());
|
||||
$representation->setStoredPubId($element->getAttribute('type'), $element->textContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeXmlSubmissionFileFilter.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 NativeXmlSubmissionFileFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Native XML document to a submission file
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\core\Services;
|
||||
use APP\facades\Repo;
|
||||
use PKP\core\Core;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\file\FileManager;
|
||||
use PKP\file\TemporaryFileManager;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\submission\GenreDAO;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
class NativeXmlSubmissionFileFilter extends NativeImportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML submission file import');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from NativeImportFilter
|
||||
//
|
||||
/**
|
||||
* Return the plural element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluralElementName()
|
||||
{
|
||||
return 'submission_files';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singular element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSingularElementName()
|
||||
{
|
||||
return 'submission_file';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a submission file element
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
*
|
||||
* @return SubmissionFile|null Null if skipping this file
|
||||
*/
|
||||
public function handleElement($node)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$submission = $deployment->getSubmission();
|
||||
$context = $deployment->getContext();
|
||||
$stageName = $node->getAttribute('stage');
|
||||
$submissionFileIdFromXml = $node->getAttribute('id');
|
||||
$stageNameIdMapping = $deployment->getStageNameStageIdMapping();
|
||||
assert(isset($stageNameIdMapping[$stageName]));
|
||||
$stageId = $stageNameIdMapping[$stageName];
|
||||
$errorOccurred = false;
|
||||
|
||||
$genreId = null;
|
||||
$genreName = $node->getAttribute('genre');
|
||||
// Build a cached list of genres by context ID by name
|
||||
if ($genreName) {
|
||||
if (!isset($genresByContextId[$context->getId()])) {
|
||||
$genreDao = DAORegistry::getDAO('GenreDAO'); /** @var GenreDAO $genreDao */
|
||||
$genres = $genreDao->getByContextId($context->getId());
|
||||
while ($genre = $genres->next()) {
|
||||
foreach ($genre->getName(null) as $locale => $name) {
|
||||
$genresByContextId[$context->getId()][$name] = $genre;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isset($genresByContextId[$context->getId()][$genreName])) {
|
||||
$deployment->addError(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, $submission->getId(), __('plugins.importexport.common.error.unknownGenre', ['param' => $genreName]));
|
||||
$errorOccurred = true;
|
||||
} else {
|
||||
$genre = $genresByContextId[$context->getId()][$genreName];
|
||||
$genreId = $genre->getId();
|
||||
}
|
||||
}
|
||||
|
||||
$uploaderUsername = $node->getAttribute('uploader');
|
||||
$uploaderUserId = null;
|
||||
if (!$uploaderUsername) {
|
||||
$user = $deployment->getUser();
|
||||
} else {
|
||||
// Determine the user based on the username
|
||||
$user = Repo::user()->getByUsername($uploaderUsername, true);
|
||||
}
|
||||
$uploaderUserId = $user
|
||||
? (int) $user->getId()
|
||||
: Application::get()->getRequest()->getUser()->getId();
|
||||
|
||||
$submissionFile = Repo::submissionFile()->dao->newDataObject();
|
||||
$submissionFile->setData('submissionId', $submission->getId());
|
||||
$submissionFile->setData('locale', $submission->getLocale());
|
||||
$submissionFile->setData('fileStage', $stageId);
|
||||
$submissionFile->setData('createdAt', Core::getCurrentDate());
|
||||
$submissionFile->setData('updatedAt', Core::getCurrentDate());
|
||||
$submissionFile->setData('dateCreated', $node->getAttribute('date_created'));
|
||||
$submissionFile->setData('language', $node->getAttribute('language'));
|
||||
|
||||
if ($caption = $node->getAttribute('caption')) {
|
||||
$submissionFile->setData('caption', $caption);
|
||||
}
|
||||
|
||||
if ($copyrightOwner = $node->getAttribute('copyright_owner')) {
|
||||
$submissionFile->setData('copyrightOwner', $copyrightOwner);
|
||||
}
|
||||
|
||||
if ($credit = $node->getAttribute('credit')) {
|
||||
$submissionFile->setData('credit', $credit);
|
||||
}
|
||||
|
||||
if (strlen($directSalesPrice = $node->getAttribute('direct_sales_price'))) {
|
||||
$submissionFile->setData('directSalesPrice', $directSalesPrice);
|
||||
}
|
||||
|
||||
if ($genreId) {
|
||||
$submissionFile->setData('genreId', $genreId);
|
||||
}
|
||||
|
||||
if ($salesType = $node->getAttribute('sales_type')) {
|
||||
$submissionFile->setData('salesType', $salesType);
|
||||
}
|
||||
|
||||
if ($sourceSubmissionFileId = $node->getAttribute('source_submission_file_id')) {
|
||||
$submissionFile->setData('sourceSubmissionFileId', $sourceSubmissionFileId);
|
||||
}
|
||||
|
||||
if ($terms = $node->getAttribute('terms')) {
|
||||
$submissionFile->setData('terms', $terms);
|
||||
}
|
||||
|
||||
if ($uploaderUserId) {
|
||||
$submissionFile->setData('uploaderUserId', $uploaderUserId);
|
||||
}
|
||||
|
||||
if ($node->getAttribute('viewable') == 'true') {
|
||||
$submissionFile->setData('viewable', true);
|
||||
}
|
||||
|
||||
// Handle metadata in sub-elements
|
||||
$fileIds = [];
|
||||
$currentFileId = null;
|
||||
for ($childNode = $node->firstChild; $childNode !== null; $childNode = $childNode->nextSibling) {
|
||||
if ($childNode instanceof \DOMElement) {
|
||||
switch ($childNode->tagName) {
|
||||
case 'id':
|
||||
$this->parseIdentifier($childNode, $submissionFile);
|
||||
break;
|
||||
case 'creator':
|
||||
case 'description':
|
||||
case 'name':
|
||||
case 'publisher':
|
||||
case 'source':
|
||||
case 'sponsor':
|
||||
case 'subject':
|
||||
[$locale, $value] = $this->parseLocalizedContent($childNode);
|
||||
$submissionFile->setData($childNode->tagName, $value, $locale);
|
||||
break;
|
||||
case 'submission_file_ref':
|
||||
if ($submissionFile->getData('fileStage') == SubmissionFile::SUBMISSION_FILE_DEPENDENT) {
|
||||
$oldAssocId = $childNode->getAttribute('id');
|
||||
$newAssocId = $deployment->getSubmissionFileDBId($oldAssocId);
|
||||
if ($newAssocId) {
|
||||
$submissionFile->setData('assocType', PKPApplication::ASSOC_TYPE_SUBMISSION_FILE);
|
||||
$submissionFile->setData('assocId', $newAssocId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'file':
|
||||
// File has already been imported so update file id
|
||||
$fileId = $deployment->getFileDBId($childNode->getAttribute('id')) ?: $this->handleRevisionElement($childNode);
|
||||
// Failed to insert the file (error messages are set at the <file> handler)
|
||||
if (!$fileId) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is the current file revision, set the submission file id
|
||||
if ($childNode->getAttribute('id') == $node->getAttribute('file_id')) {
|
||||
$currentFileId = $fileId;
|
||||
} else { // Otherwise add it to the list of previous revisions
|
||||
$fileIds[] = $fileId;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownElement', ['param' => $node->tagName]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Quit if there were errors or if the main file could not be inserted
|
||||
if ($errorOccurred || !$currentFileId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure the current file revision is the last to be processed
|
||||
$fileIds[] = $currentFileId;
|
||||
|
||||
// Consumes the first file ID to insert an initial submission file
|
||||
$submissionFile->setData('fileId', array_shift($fileIds));
|
||||
$submissionFile = Repo::submissionFile()->get(Repo::submissionFile()->add($submissionFile));
|
||||
|
||||
// Edits the submission file revisions one-by-one so that a useful activity log is built and past revisions can be accessed
|
||||
foreach ($fileIds as $fileId) {
|
||||
Repo::submissionFile()->edit($submissionFile, ['fileId' => $fileId]);
|
||||
}
|
||||
|
||||
$deployment->setSubmissionFileDBId($submissionFileIdFromXml, $submissionFile->getId());
|
||||
|
||||
// Retrieves the updated submission file
|
||||
return Repo::submissionFile()->get($submissionFile->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a revision element
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
*
|
||||
* @return int|null The new file id if successful
|
||||
*/
|
||||
public function handleRevisionElement($node)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$submission = $deployment->getSubmission();
|
||||
|
||||
for ($childNode = $node->firstChild; $childNode !== null; $childNode = $childNode->nextSibling) {
|
||||
if ($childNode instanceof \DOMElement) {
|
||||
switch ($childNode->tagName) {
|
||||
case 'href':
|
||||
$temporaryFileManager = new TemporaryFileManager();
|
||||
$temporaryFilename = tempnam($temporaryFileManager->getBasePath(), 'src');
|
||||
$filesrc = $childNode->getAttribute('src');
|
||||
$errorFlag = false;
|
||||
if (preg_match('|\w+://.+|', $filesrc)) {
|
||||
// process as a URL
|
||||
$client = Application::get()->getHttpClient();
|
||||
$response = $client->request('GET', $filesrc);
|
||||
file_put_contents($temporaryFilename, $response->getBody());
|
||||
if (!filesize($temporaryFilename)) {
|
||||
$errorFlag = true;
|
||||
}
|
||||
} elseif (substr($filesrc, 0, 1) === '/') {
|
||||
// local file (absolute path)
|
||||
if (!copy($filesrc, $temporaryFilename)) {
|
||||
$errorFlag = true;
|
||||
}
|
||||
} elseif (is_readable($deployment->getImportPath() . '/' . $filesrc)) {
|
||||
// local file (relative path)
|
||||
$filesrc = $deployment->getImportPath() . '/' . $filesrc;
|
||||
if (!copy($filesrc, $temporaryFilename)) {
|
||||
$errorFlag = true;
|
||||
}
|
||||
} else {
|
||||
// unhandled file path
|
||||
$errorFlag = true;
|
||||
}
|
||||
if ($errorFlag) {
|
||||
$deployment->addError(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.temporaryFileFailed', ['dest' => $temporaryFilename, 'source' => $filesrc]));
|
||||
$fileManager = new FileManager();
|
||||
$fileManager->deleteByPath($temporaryFilename);
|
||||
$temporaryFilename = '';
|
||||
}
|
||||
break;
|
||||
case 'embed':
|
||||
$temporaryFileManager = new TemporaryFileManager();
|
||||
$temporaryFilename = tempnam($temporaryFileManager->getBasePath(), 'embed');
|
||||
if (($e = $childNode->getAttribute('encoding')) != 'base64') {
|
||||
$deployment->addError(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownEncoding', ['param' => $e]));
|
||||
} else {
|
||||
$content = base64_decode($childNode->textContent, true);
|
||||
$errorFlag = false;
|
||||
if (!$content) {
|
||||
$deployment->addError(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.encodingError', ['param' => $e]));
|
||||
$errorFlag = true;
|
||||
} elseif (!file_put_contents($temporaryFilename, $content)) {
|
||||
$deployment->addError(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.temporaryFileFailed', ['dest' => $temporaryFilename, 'source' => 'embed']));
|
||||
$errorFlag = true;
|
||||
}
|
||||
if ($errorFlag) {
|
||||
$fileManager = new FileManager();
|
||||
$fileManager->deleteByPath($temporaryFilename);
|
||||
$temporaryFilename = '';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$newFileId = null;
|
||||
if ($temporaryFilename) {
|
||||
$fileSizeOnDisk = filesize($temporaryFilename);
|
||||
$expectedFileSize = $node->getAttribute('filesize');
|
||||
if ($fileSizeOnDisk != $expectedFileSize) {
|
||||
$deployment->addWarning(Application::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.filesizeMismatch', ['expected' => $expectedFileSize, 'actual' => $fileSizeOnDisk]));
|
||||
}
|
||||
clearstatcache(true, $temporaryFilename);
|
||||
$fileManager = new FileManager();
|
||||
$submissionDir = Repo::submissionFile()->getSubmissionDir($submission->getData('contextId'), $submission->getId());
|
||||
$newFileId = Services::get('file')->add(
|
||||
$temporaryFilename,
|
||||
$submissionDir . '/' . uniqid() . '.' . $node->getAttribute('extension')
|
||||
);
|
||||
$deployment->setFileDBId($node->getAttribute('id'), $newFileId);
|
||||
$fileManager = new FileManager();
|
||||
$fileManager->deleteByPath($temporaryFilename);
|
||||
}
|
||||
|
||||
return $newFileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an identifier node and set up the representation object accordingly
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param SubmissionFile $submissionFile
|
||||
*/
|
||||
public function parseIdentifier($element, $submissionFile)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
$advice = $element->getAttribute('advice');
|
||||
switch ($element->getAttribute('type')) {
|
||||
case 'internal':
|
||||
// "update" advice not supported yet.
|
||||
assert(!$advice || $advice == 'ignore');
|
||||
break;
|
||||
case 'public':
|
||||
if ($advice == 'update') {
|
||||
$submissionFile->setStoredPubId('publisher-id', $element->textContent);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($advice == 'update') {
|
||||
if ($element->getAttribute('type') == 'doi') {
|
||||
$doiFound = Repo::doi()->getCollector()->filterByIdentifier($element->textContent)->getMany()->first();
|
||||
if ($doiFound) {
|
||||
$submissionFile->setData('doiId', $doiFound->getId());
|
||||
} else {
|
||||
$newDoiObject = Repo::doi()->newDataObject(
|
||||
[
|
||||
'doi' => $element->textContent,
|
||||
'contextId' => $context->getId()
|
||||
]
|
||||
);
|
||||
$doiId = Repo::doi()->add($newDoiObject);
|
||||
$submissionFile->setData('doiId', $doiId);
|
||||
}
|
||||
} else {
|
||||
// Load pub id plugins
|
||||
PluginRegistry::loadCategory('pubIds', true, $context->getId());
|
||||
$submissionFile->setStoredPubId($element->getAttribute('type'), $element->textContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a submission file.
|
||||
*
|
||||
* @param string $tagName
|
||||
*
|
||||
* @return SubmissionFile
|
||||
*/
|
||||
public function instantiateSubmissionFile($tagName)
|
||||
{
|
||||
assert(false); // Subclasses should override
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/NativeXmlSubmissionFilter.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 NativeXmlSubmissionFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Native XML document to a set of submissions
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use APP\submission\Submission;
|
||||
use PKP\core\Core;
|
||||
use PKP\filter\Filter;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\observers\events\BatchMetadataChanged;
|
||||
use PKP\workflow\WorkflowStageDAO;
|
||||
|
||||
class NativeXmlSubmissionFilter extends NativeImportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML submission import');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from NativeImportFilter
|
||||
//
|
||||
/**
|
||||
* Return the plural element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPluralElementName()
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
return $deployment->getSubmissionsNodeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singular element name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSingularElementName()
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
return $deployment->getSubmissionNodeName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a singular element import.
|
||||
*
|
||||
* @param \DOMElement $node
|
||||
*/
|
||||
public function handleElement($node)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
|
||||
// Create and insert the submission (ID needed for other entities)
|
||||
$submission = Repo::submission()->newDataObject();
|
||||
|
||||
$submission->setData('locale', $node->getAttribute('locale') ?: $context->getPrimaryLocale());
|
||||
$submission->setData('contextId', $context->getId());
|
||||
$submission->stampLastActivity();
|
||||
$submission->stampModified();
|
||||
$submission->setData('status', $node->getAttribute('status'));
|
||||
$submission->setData('submissionProgress', '');
|
||||
|
||||
$submission->setData('stageId', WorkflowStageDAO::getIdFromPath($node->getAttribute('stage')));
|
||||
|
||||
// Handle any additional attributes etc.
|
||||
$submission = $this->populateObject($submission, $node);
|
||||
|
||||
$submissionId = Repo::submission()->dao->insert($submission);
|
||||
$submission = Repo::submission()->get($submissionId);
|
||||
// Non-persisted temporary ID, will be updated once the publication gets parsed
|
||||
$submission->setData('currentPublicationId', $node->getAttribute('current_publication_id'));
|
||||
$deployment->setSubmission($submission);
|
||||
$deployment->addProcessedObjectId(Application::ASSOC_TYPE_SUBMISSION, $submission->getId());
|
||||
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof \DOMElement) {
|
||||
$this->handleChildElement($n, $submission);
|
||||
}
|
||||
}
|
||||
|
||||
$submission = Repo::submission()->get($submission->getId());
|
||||
|
||||
$deployment->addImportedRootEntity(Application::ASSOC_TYPE_SUBMISSION, $submission);
|
||||
|
||||
return $submission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the submission object from the node
|
||||
*
|
||||
* @param Submission $submission
|
||||
* @param \DOMElement $node
|
||||
*
|
||||
* @return Submission
|
||||
*/
|
||||
public function populateObject($submission, $node)
|
||||
{
|
||||
if ($dateSubmitted = $node->getAttribute('date_submitted')) {
|
||||
$submission->setData('dateSubmitted', Core::getCurrentDate(strtotime($dateSubmitted)));
|
||||
} else {
|
||||
$submission->setData('dateSubmitted', Core::getCurrentDate());
|
||||
}
|
||||
|
||||
return $submission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an element whose parent is the submission element.
|
||||
*
|
||||
* @param \DOMElement $n
|
||||
* @param Submission $submission
|
||||
*/
|
||||
public function handleChildElement($n, $submission)
|
||||
{
|
||||
switch ($n->tagName) {
|
||||
case 'id':
|
||||
$this->parseIdentifier($n, $submission);
|
||||
break;
|
||||
case 'submission_file':
|
||||
$this->parseChild($n, $submission);
|
||||
break;
|
||||
case 'publication':
|
||||
$this->parseChild($n, $submission);
|
||||
break;
|
||||
default:
|
||||
$deployment = $this->getDeployment();
|
||||
$deployment->addWarning(Application::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.unknownElement', ['param' => $n->tagName]));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Element parsing
|
||||
//
|
||||
/**
|
||||
* Parse an identifier node and set up the submission object accordingly
|
||||
*
|
||||
* @param \DOMElement $element
|
||||
* @param Submission $submission
|
||||
*/
|
||||
public function parseIdentifier($element, $submission)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$advice = $element->getAttribute('advice');
|
||||
switch ($element->getAttribute('type')) {
|
||||
case 'internal':
|
||||
// "update" advice not supported yet.
|
||||
assert(!$advice || $advice == 'ignore');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param \DOMDocument|string $document
|
||||
*
|
||||
* @return array Array of imported documents
|
||||
*/
|
||||
public function &process(&$document)
|
||||
{
|
||||
$importedObjects = & parent::process($document);
|
||||
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
// Index imported content
|
||||
$submissionIds = [];
|
||||
foreach ($importedObjects as $submission) {
|
||||
assert($submission instanceof Submission);
|
||||
$publication = $submission->getCurrentPublication();
|
||||
if (!isset($publication)) {
|
||||
$deployment->addError(Application::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.currentPublicationNullOrMissing'));
|
||||
}
|
||||
$submissionIds[] = $submission->getId();
|
||||
}
|
||||
|
||||
event(new BatchMetadataChanged($submissionIds));
|
||||
|
||||
return $importedObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a submission child and add it to the submission.
|
||||
*
|
||||
* @param \DOMElement $n
|
||||
* @param Submission $submission
|
||||
*/
|
||||
public function parseChild($n, $submission)
|
||||
{
|
||||
$importFilter = $this->getImportFilter($n->tagName);
|
||||
assert(isset($importFilter)); // There should be a filter
|
||||
|
||||
$submissionChildDoc = new \DOMDocument('1.0', 'utf-8');
|
||||
$submissionChildDoc->appendChild($submissionChildDoc->importNode($n, true));
|
||||
$ret = $importFilter->execute($submissionChildDoc);
|
||||
|
||||
if ($ret == null) {
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
$deployment->addError(Application::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.common.error.submissionChildFailed', ['child' => $n->tagName]));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Helper functions
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the import filter for a given element.
|
||||
*
|
||||
* @param string $elementName Name of XML element
|
||||
*
|
||||
* @return Filter
|
||||
*/
|
||||
public function getImportFilter($elementName)
|
||||
{
|
||||
assert(false); // Subclasses should override
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/PKPAuthorNativeXmlFilter.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 PKPAuthorNativeXmlFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a set of authors to a Native XML document
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use Exception;
|
||||
use PKP\filter\FilterGroup;
|
||||
|
||||
class PKPAuthorNativeXmlFilter extends NativeExportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML author export');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param array $authors Array of authors
|
||||
*
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public function &process(&$authors)
|
||||
{
|
||||
// Create the XML document
|
||||
$doc = new \DOMDocument('1.0', 'utf-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
// Multiple authors; wrap in a <authors> element
|
||||
$rootNode = $doc->createElementNS($deployment->getNamespace(), 'authors');
|
||||
foreach ($authors as $author) {
|
||||
$rootNode->appendChild($this->createPKPAuthorNode($doc, $author));
|
||||
}
|
||||
$doc->appendChild($rootNode);
|
||||
$rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$rootNode->setAttribute('xsi:schemaLocation', $deployment->getNamespace() . ' ' . $deployment->getSchemaFilename());
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
//
|
||||
// PKPAuthor conversion functions
|
||||
//
|
||||
/**
|
||||
* Create and return an author node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \PKP\author\Author $author
|
||||
*
|
||||
* @return \DOMElement
|
||||
*/
|
||||
public function createPKPAuthorNode($doc, $author)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
|
||||
// Create the author node
|
||||
$authorNode = $doc->createElementNS($deployment->getNamespace(), 'author');
|
||||
|
||||
if ($author->getPrimaryContact()) {
|
||||
$authorNode->setAttribute('primary_contact', 'true');
|
||||
}
|
||||
if ($author->getIncludeInBrowse()) {
|
||||
$authorNode->setAttribute('include_in_browse', 'true');
|
||||
}
|
||||
|
||||
$userGroup = Repo::userGroup()->get($author->getUserGroupId());
|
||||
assert(isset($userGroup));
|
||||
|
||||
if (!$userGroup) {
|
||||
$deployment->addError(Application::ASSOC_TYPE_AUTHOR, $author->getId(), __('plugins.importexport.common.error.userGroupMissing', ['param' => $author->getFullName()]));
|
||||
throw new Exception(__('plugins.importexport.author.exportFailed'));
|
||||
}
|
||||
|
||||
$authorNode->setAttribute('user_group_ref', $userGroup->getName($context->getPrimaryLocale()));
|
||||
$authorNode->setAttribute('seq', $author->getSequence());
|
||||
|
||||
$authorNode->setAttribute('id', $author->getId());
|
||||
|
||||
// Add metadata
|
||||
$this->createLocalizedNodes($doc, $authorNode, 'givenname', $author->getGivenName(null));
|
||||
$this->createLocalizedNodes($doc, $authorNode, 'familyname', $author->getFamilyName(null));
|
||||
|
||||
$this->createLocalizedNodes($doc, $authorNode, 'affiliation', $author->getAffiliation(null));
|
||||
|
||||
$this->createOptionalNode($doc, $authorNode, 'country', $author->getCountry());
|
||||
$authorNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'email', htmlspecialchars($author->getEmail(), ENT_COMPAT, 'UTF-8')));
|
||||
$this->createOptionalNode($doc, $authorNode, 'url', $author->getUrl());
|
||||
$this->createOptionalNode($doc, $authorNode, 'orcid', $author->getOrcid());
|
||||
|
||||
$this->createLocalizedNodes($doc, $authorNode, 'biography', $author->getBiography(null));
|
||||
|
||||
return $authorNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/PKPNativeFilterHelper.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 PKPNativeFilterHelper
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Class that provides native import/export filter-related helper methods.
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\file\PublicFileManager;
|
||||
use APP\publication\Publication;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use PKP\core\PKPApplication;
|
||||
|
||||
class PKPNativeFilterHelper
|
||||
{
|
||||
/**
|
||||
* Create and return an object covers node.
|
||||
*/
|
||||
public function createPublicationCoversNode(NativeExportFilter $filter, DOMDocument $doc, Publication $object): ?DOMElement
|
||||
{
|
||||
$coverImages = $object->getData('coverImage');
|
||||
if (empty($coverImages)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$deployment = $filter->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
$publicFileManager = new PublicFileManager();
|
||||
$contextId = $context->getId();
|
||||
$coversNode = $doc->createElementNS($deployment->getNamespace(), 'covers');
|
||||
foreach ($coverImages as $locale => $coverImage) {
|
||||
$coverImageName = $coverImage['uploadName'] ?? '';
|
||||
|
||||
$filePath = $publicFileManager->getContextFilesPath($contextId) . '/' . $coverImageName;
|
||||
if (!file_exists($filePath)) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_PUBLICATION, $object->getId(), __('plugins.importexport.common.error.publicationCoverImageMissing', ['id' => $object->getId(), 'path' => $filePath]));
|
||||
continue;
|
||||
}
|
||||
|
||||
$coverNode = $doc->createElementNS($deployment->getNamespace(), 'cover');
|
||||
$coverNode->setAttribute('locale', $locale);
|
||||
$coverNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'cover_image', htmlspecialchars($coverImageName, ENT_COMPAT, 'UTF-8')));
|
||||
$coverNode->appendChild($doc->createElementNS($deployment->getNamespace(), 'cover_image_alt_text', htmlspecialchars($coverImage['altText'] ?? '', ENT_COMPAT, 'UTF-8')));
|
||||
|
||||
$embedNode = $doc->createElementNS($deployment->getNamespace(), 'embed', base64_encode(file_get_contents($filePath)));
|
||||
$embedNode->setAttribute('encoding', 'base64');
|
||||
$coverNode->appendChild($embedNode);
|
||||
$coversNode->appendChild($coverNode);
|
||||
}
|
||||
|
||||
return $coversNode->firstChild?->parentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse out the object covers.
|
||||
*
|
||||
* @param NativeImportFilter $filter
|
||||
* @param \DOMElement $node
|
||||
* @param Publication $object
|
||||
*/
|
||||
public function parsePublicationCovers($filter, $node, $object)
|
||||
{
|
||||
$deployment = $filter->getDeployment();
|
||||
|
||||
$coverImages = [];
|
||||
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof DOMElement) {
|
||||
switch ($n->tagName) {
|
||||
case 'cover':
|
||||
$coverImage = $this->parsePublicationCover($filter, $n, $object);
|
||||
$coverImages[key($coverImage)] = reset($coverImage);
|
||||
break;
|
||||
default:
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_PUBLICATION, $object->getId(), __('plugins.importexport.common.error.unknownElement', ['param' => $n->tagName]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$object->setData('coverImage', $coverImages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse out the cover and store it in the object.
|
||||
*
|
||||
* @param NativeImportFilter $filter
|
||||
* @param \DOMElement $node
|
||||
* @param Publication $object
|
||||
*/
|
||||
public function parsePublicationCover($filter, $node, $object)
|
||||
{
|
||||
$deployment = $filter->getDeployment();
|
||||
|
||||
$context = $deployment->getContext();
|
||||
|
||||
$locale = $node->getAttribute('locale');
|
||||
if (empty($locale)) {
|
||||
$locale = $context->getPrimaryLocale();
|
||||
}
|
||||
|
||||
$coverImagelocale = [];
|
||||
$coverImage = [];
|
||||
|
||||
for ($n = $node->firstChild; $n !== null; $n = $n->nextSibling) {
|
||||
if ($n instanceof DOMElement) {
|
||||
switch ($n->tagName) {
|
||||
case 'cover_image':
|
||||
$coverImage['uploadName'] = trim(
|
||||
preg_replace(
|
||||
"/[^a-z0-9\.\-]+/",
|
||||
"",
|
||||
str_replace(
|
||||
[' ', '_', ':'],
|
||||
'-',
|
||||
strtolower($n->textContent)
|
||||
)
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 'cover_image_alt_text':
|
||||
$coverImage['altText'] = $n->textContent;
|
||||
break;
|
||||
case 'embed':
|
||||
if (!isset($coverImage['uploadName'])) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_PUBLICATION, $object->getId(), __('plugins.importexport.common.error.coverImageNameUnspecified'));
|
||||
break;
|
||||
}
|
||||
|
||||
$publicFileManager = new PublicFileManager();
|
||||
$filePath = $publicFileManager->getContextFilesPath($context->getId()) . '/' . $coverImage['uploadName'];
|
||||
$allowedFileTypes = ['gif', 'jpg', 'png', 'webp'];
|
||||
$extension = pathinfo(strtolower($filePath), PATHINFO_EXTENSION);
|
||||
if (!in_array($extension, $allowedFileTypes)) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_PUBLICATION, $object->getId(), __('plugins.importexport.common.error.invalidFileExtension'));
|
||||
break;
|
||||
}
|
||||
file_put_contents($filePath, base64_decode($n->textContent));
|
||||
break;
|
||||
default:
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_PUBLICATION, $object->getId(), __('plugins.importexport.common.error.unknownElement', ['param' => $n->tagName]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$coverImagelocale[$locale] = $coverImage;
|
||||
|
||||
return $coverImagelocale;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/PKPPublicationNativeXmlFilter.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 PKPPublicationNativeXmlFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a Publication to a Native XML document
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\plugins\importexport\native\NativeImportExportDeployment;
|
||||
use APP\publication\Publication;
|
||||
use Exception;
|
||||
use PKP\citation\CitationDAO;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\importexport\PKPImportExportFilter;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\submission\PKPSubmission;
|
||||
use PKP\submission\Representation;
|
||||
use PKP\submission\RepresentationDAOInterface;
|
||||
|
||||
class PKPPublicationNativeXmlFilter extends NativeExportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML Publication export');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param Publication $entity
|
||||
*
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public function &process(&$entity)
|
||||
{
|
||||
// Create the XML document
|
||||
$doc = new \DOMDocument('1.0', 'utf-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
$deployment = $this->getDeployment();
|
||||
$rootNode = $this->createEntityNode($doc, $entity);
|
||||
$doc->appendChild($rootNode);
|
||||
$rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$rootNode->setAttribute('xsi:schemaLocation', $deployment->getNamespace() . ' ' . $deployment->getSchemaFilename());
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
//
|
||||
// Representation conversion functions
|
||||
//
|
||||
/**
|
||||
* Create and return an entity node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param Publication $entity
|
||||
*
|
||||
* @return \DOMElement
|
||||
*/
|
||||
public function createEntityNode($doc, $entity)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
|
||||
// Create the entity node
|
||||
$entityNode = $doc->createElementNS($deployment->getNamespace(), 'publication');
|
||||
|
||||
$this->addIdentifiers($doc, $entityNode, $entity);
|
||||
|
||||
$entityNode->setAttribute('version', $entity->getData('version') ?: 1);
|
||||
$entityNode->setAttribute('status', $entity->getData('status'));
|
||||
if ($primaryContactId = $entity->getData('primaryContactId')) {
|
||||
$entityNode->setAttribute('primary_contact_id', $primaryContactId);
|
||||
}
|
||||
$entityNode->setAttribute('url_path', $entity->getData('urlPath'));
|
||||
|
||||
if ($entity->getData('status') === PKPSubmission::STATUS_PUBLISHED) {
|
||||
$entityNode->setAttribute('seq', (int) $entity->getData('seq'));
|
||||
} else {
|
||||
$entityNode->setAttribute('seq', '0');
|
||||
}
|
||||
|
||||
if ($entity->getData('accessStatus')) {
|
||||
$entityNode->setAttribute('access_status', $entity->getData('accessStatus'));
|
||||
} else {
|
||||
$entityNode->setAttribute('access_status', '0');
|
||||
}
|
||||
|
||||
if ($datePublished = $entity->getData('datePublished')) {
|
||||
$entityNode->setAttribute('date_published', date('Y-m-d', strtotime($datePublished)));
|
||||
}
|
||||
|
||||
$this->addMetadata($doc, $entityNode, $entity);
|
||||
|
||||
$authors = $entity->getData('authors');
|
||||
if ($authors && count($authors) > 0) {
|
||||
$this->addAuthors($doc, $entityNode, $entity);
|
||||
}
|
||||
|
||||
$this->addRepresentations($doc, $entityNode, $entity);
|
||||
|
||||
$citationsListNode = $this->createCitationsNode($doc, $deployment, $entity);
|
||||
if ($citationsListNode->hasChildNodes() || $citationsListNode->hasAttributes()) {
|
||||
$entityNode->appendChild($citationsListNode);
|
||||
}
|
||||
|
||||
return $entityNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and add identifier nodes to a submission node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $entityNode
|
||||
* @param Publication $entity
|
||||
*/
|
||||
public function addIdentifiers($doc, $entityNode, $entity)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
// Add internal ID
|
||||
$entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', $entity->getId()));
|
||||
$node->setAttribute('type', 'internal');
|
||||
$node->setAttribute('advice', 'ignore');
|
||||
|
||||
// Add public ID
|
||||
if ($pubId = $entity->getStoredPubId('publisher-id')) {
|
||||
$entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', htmlspecialchars($pubId, ENT_COMPAT, 'UTF-8')));
|
||||
$node->setAttribute('type', 'public');
|
||||
$node->setAttribute('advice', 'update');
|
||||
}
|
||||
|
||||
// Add pub IDs by plugin
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $deployment->getContext()->getId());
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$this->addPubIdentifier($doc, $entityNode, $entity, $pubIdPlugin->getPubIdType());
|
||||
}
|
||||
// Also add DOI
|
||||
$this->addPubIdentifier($doc, $entityNode, $entity, 'doi');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single pub ID element for a given plugin to the document.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $entityNode
|
||||
* @param Publication $entity
|
||||
*
|
||||
* @return ?\DOMElement
|
||||
*/
|
||||
public function addPubIdentifier($doc, $entityNode, $entity, $pubIdType)
|
||||
{
|
||||
$pubId = $entity->getStoredPubId($pubIdType);
|
||||
if ($pubId) {
|
||||
$deployment = $this->getDeployment();
|
||||
$entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', htmlspecialchars($pubId, ENT_COMPAT, 'UTF-8')));
|
||||
$node->setAttribute('type', $pubIdType);
|
||||
$node->setAttribute('advice', 'update');
|
||||
return $node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the publication metadata for a publication to its DOM element.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $entityNode
|
||||
* @param Publication $entity
|
||||
*/
|
||||
public function addMetadata($doc, $entityNode, $entity)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'title', $entity->getTitles('html'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'prefix', $entity->getData('prefix'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'subtitle', $entity->getSubTitles('html'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'abstract', $entity->getData('abstract'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'coverage', $entity->getData('coverage'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'type', $entity->getData('type'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'source', $entity->getData('source'));
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'rights', $entity->getData('rights'));
|
||||
|
||||
if ($entity->getData('licenseUrl')) {
|
||||
$entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'licenseUrl', htmlspecialchars($entity->getData('licenseUrl'))));
|
||||
}
|
||||
|
||||
$this->createLocalizedNodes($doc, $entityNode, 'copyrightHolder', $entity->getData('copyrightHolder'));
|
||||
|
||||
if ($entity->getData('copyrightYear')) {
|
||||
$entityNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'copyrightYear', intval($entity->getData('copyrightYear'))));
|
||||
}
|
||||
|
||||
// add controlled vocabularies
|
||||
// get the supported locale keys
|
||||
$supportedLocales = $deployment->getContext()->getSupportedFormLocales();
|
||||
$controlledVocabulariesMapping = $this->_getControlledVocabulariesMappings();
|
||||
foreach ($controlledVocabulariesMapping as $controlledVocabulariesNodeName => $mappings) {
|
||||
$dao = DAORegistry::getDAO($mappings[0]);
|
||||
$getFunction = $mappings[1];
|
||||
$controlledVocabularyNodeName = $mappings[2];
|
||||
$controlledVocabulary = $dao->$getFunction($entity->getId(), $supportedLocales);
|
||||
$this->addControlledVocabulary($doc, $entityNode, $controlledVocabulariesNodeName, $controlledVocabularyNodeName, $controlledVocabulary);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add publication's controlled vocabulary to its DOM element.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $entityNode
|
||||
* @param string $controlledVocabulariesNodeName Parent node name
|
||||
* @param string $controlledVocabularyNodeName Item node name
|
||||
* @param array $controlledVocabulary Associative array (locale => array of items)
|
||||
*/
|
||||
public function addControlledVocabulary($doc, $entityNode, $controlledVocabulariesNodeName, $controlledVocabularyNodeName, $controlledVocabulary)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$locales = array_keys($controlledVocabulary);
|
||||
foreach ($locales as $locale) {
|
||||
if (!empty($controlledVocabulary[$locale])) {
|
||||
$controlledVocabulariesNode = $doc->createElementNS($deployment->getNamespace(), $controlledVocabulariesNodeName);
|
||||
$controlledVocabulariesNode->setAttribute('locale', $locale);
|
||||
foreach ($controlledVocabulary[$locale] as $controlledVocabularyItem) {
|
||||
$controlledVocabulariesNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), $controlledVocabularyNodeName, htmlspecialchars($controlledVocabularyItem, ENT_COMPAT, 'UTF-8')));
|
||||
}
|
||||
|
||||
$entityNode->appendChild($controlledVocabulariesNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the author metadata for a submission to its DOM element.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $entityNode
|
||||
* @param Publication $entity
|
||||
*/
|
||||
public function addAuthors($doc, $entityNode, $entity)
|
||||
{
|
||||
$currentFilter = PKPImportExportFilter::getFilter('author=>native-xml', $this->getDeployment());
|
||||
|
||||
$authors = $entity->getData('authors')->toArray();
|
||||
$authorsDoc = $currentFilter->execute($authors);
|
||||
|
||||
if ($authorsDoc && $authorsDoc->documentElement instanceof \DOMElement) {
|
||||
$clone = $doc->importNode($authorsDoc->documentElement, true);
|
||||
$entityNode->appendChild($clone);
|
||||
} else {
|
||||
$deployment = $this->getDeployment();
|
||||
$deployment->addError(Application::ASSOC_TYPE_PUBLICATION, $entity->getId(), __('plugins.importexport.author.exportFailed'));
|
||||
|
||||
throw new Exception(__('plugins.importexport.author.exportFailed'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the representations of a publication to its DOM element.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $entityNode
|
||||
* @param Publication $entity
|
||||
*/
|
||||
public function addRepresentations($doc, $entityNode, $entity)
|
||||
{
|
||||
$currentFilter = PKPImportExportFilter::getFilter($this->getRepresentationExportFilterGroupName(), $this->getDeployment());
|
||||
|
||||
/** @var RepresentationDAOInterface $representationDao */
|
||||
$representationDao = Application::getRepresentationDAO();
|
||||
$representations = $representationDao->getByPublicationId($entity->getId());
|
||||
foreach ($representations as $representation) {
|
||||
$representationDoc = $currentFilter->execute($representation);
|
||||
$clone = $doc->importNode($representationDoc->documentElement, true);
|
||||
$entityNode->appendChild($clone);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controlled vocabularies parent node name to DAO, get function and item node name mapping.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function _getControlledVocabulariesMappings()
|
||||
{
|
||||
return [
|
||||
'keywords' => ['SubmissionKeywordDAO', 'getKeywords', 'keyword'],
|
||||
'agencies' => ['SubmissionAgencyDAO', 'getAgencies', 'agency'],
|
||||
'languages' => ['SubmissionLanguageDAO', 'getLanguages', 'language'],
|
||||
'disciplines' => ['SubmissionDisciplineDAO', 'getDisciplines', 'discipline'],
|
||||
'subjects' => ['SubmissionSubjectDAO', 'getSubjects', 'subject'],
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
// Abstract methods to be implemented by subclasses
|
||||
//
|
||||
/**
|
||||
* Get the submission files associated with this representation
|
||||
*
|
||||
* @param Representation $representation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFiles($representation)
|
||||
{
|
||||
assert(false); // To be overridden by subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a Citations node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param NativeImportExportDeployment $deployment
|
||||
* @param Publication $publication
|
||||
*
|
||||
* @return \DOMElement
|
||||
*/
|
||||
private function createCitationsNode($doc, $deployment, $publication)
|
||||
{
|
||||
$citationDao = DAORegistry::getDAO('CitationDAO'); /** @var CitationDAO $citationDao */
|
||||
|
||||
$nodeCitations = $doc->createElementNS($deployment->getNamespace(), 'citations');
|
||||
$submissionCitations = $citationDao->getByPublicationId($publication->getId())->toAssociativeArray();
|
||||
|
||||
foreach ($submissionCitations as $submissionCitation) {
|
||||
$rawCitation = $submissionCitation->getRawCitation();
|
||||
$nodeCitations->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'citation', htmlspecialchars($rawCitation, ENT_COMPAT, 'UTF-8')));
|
||||
}
|
||||
|
||||
return $nodeCitations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/RepresentationNativeXmlFilter.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 RepresentationNativeXmlFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a representation to a Native XML document
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\submission\Representation;
|
||||
|
||||
class RepresentationNativeXmlFilter extends NativeExportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML representation export');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param Representation $representation
|
||||
*
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public function &process(&$representation)
|
||||
{
|
||||
// Create the XML document
|
||||
$doc = new \DOMDocument('1.0', 'utf-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
$deployment = $this->getDeployment();
|
||||
$rootNode = $this->createRepresentationNode($doc, $representation);
|
||||
$doc->appendChild($rootNode);
|
||||
$rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$rootNode->setAttribute('xsi:schemaLocation', $deployment->getNamespace() . ' ' . $deployment->getSchemaFilename());
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
//
|
||||
// Representation conversion functions
|
||||
//
|
||||
/**
|
||||
* Create and return a representation node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param Representation $representation
|
||||
*
|
||||
* @return \DOMElement
|
||||
*/
|
||||
public function createRepresentationNode($doc, $representation)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
|
||||
// Create the representation node
|
||||
$representationNode = $doc->createElementNS($deployment->getNamespace(), $deployment->getRepresentationNodeName());
|
||||
|
||||
$representationNode->setAttribute('locale', $representation->getData('locale'));
|
||||
$representationNode->setAttribute('url_path', $representation->getData('urlPath'));
|
||||
|
||||
$this->addIdentifiers($doc, $representationNode, $representation);
|
||||
|
||||
// Add metadata
|
||||
$this->createLocalizedNodes($doc, $representationNode, 'name', $representation->getName(null));
|
||||
$sequenceNode = $doc->createElementNS($deployment->getNamespace(), 'seq');
|
||||
$sequenceNode->appendChild($doc->createTextNode((int) $representation->getSequence()));
|
||||
$representationNode->appendChild($sequenceNode);
|
||||
|
||||
$urlRemote = $representation->getData('urlRemote');
|
||||
if ($urlRemote) {
|
||||
$remoteNode = $doc->createElementNS($deployment->getNamespace(), 'remote');
|
||||
$remoteNode->setAttribute('src', $urlRemote);
|
||||
$representationNode->appendChild($remoteNode);
|
||||
} else {
|
||||
// Add files
|
||||
foreach ($this->getFiles($representation) as $submissionFile) {
|
||||
$fileRefNode = $doc->createElementNS($deployment->getNamespace(), 'submission_file_ref');
|
||||
$fileRefNode->setAttribute('id', $submissionFile->getId());
|
||||
$representationNode->appendChild($fileRefNode);
|
||||
}
|
||||
}
|
||||
|
||||
return $representationNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and add identifier nodes to a representation node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $representationNode
|
||||
* @param Representation $representation
|
||||
*/
|
||||
public function addIdentifiers($doc, $representationNode, $representation)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
// Add internal ID
|
||||
$representationNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', $representation->getId()));
|
||||
$node->setAttribute('type', 'internal');
|
||||
$node->setAttribute('advice', 'ignore');
|
||||
|
||||
// Add public ID
|
||||
if ($pubId = $representation->getStoredPubId('publisher-id')) {
|
||||
$representationNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', htmlspecialchars($pubId, ENT_COMPAT, 'UTF-8')));
|
||||
$node->setAttribute('type', 'public');
|
||||
$node->setAttribute('advice', 'update');
|
||||
}
|
||||
|
||||
// Add pub IDs by plugin
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $deployment->getContext()->getId());
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$this->addPubIdentifier($doc, $representationNode, $representation, $pubIdPlugin->getPubIdType());
|
||||
}
|
||||
// Also add DOI
|
||||
$this->addPubIdentifier($doc, $representationNode, $representation, 'doi');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single pub ID element for a given plugin to the representation.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $representationNode
|
||||
* @param Representation $representation
|
||||
*
|
||||
* @return ?\DOMElement
|
||||
*/
|
||||
public function addPubIdentifier($doc, $representationNode, $representation, $pubIdType)
|
||||
{
|
||||
$pubId = $representation->getStoredPubId($pubIdType);
|
||||
if ($pubId) {
|
||||
$deployment = $this->getDeployment();
|
||||
$representationNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', htmlspecialchars($pubId, ENT_COMPAT, 'UTF-8')));
|
||||
$node->setAttribute('type', $pubIdType);
|
||||
$node->setAttribute('advice', 'update');
|
||||
return $node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//
|
||||
// Abstract methods to be implemented by subclasses
|
||||
//
|
||||
/**
|
||||
* Get the submission files associated with this representation
|
||||
*
|
||||
* @param Representation $representation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFiles($representation)
|
||||
{
|
||||
assert(false); // To be overridden by subclasses
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/SubmissionFileNativeXmlFilter.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 SubmissionFileNativeXmlFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a submissionFile to a Native XML document
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\core\Application;
|
||||
use APP\facades\Repo;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use PKP\config\Config;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\PluginRegistry;
|
||||
use PKP\submission\GenreDAO;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
|
||||
class SubmissionFileNativeXmlFilter extends NativeExportFilter
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML submission file export');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param SubmissionFile $submissionFile
|
||||
*
|
||||
* @return ?DOMDocument
|
||||
*/
|
||||
public function &process(&$submissionFile)
|
||||
{
|
||||
// Create the XML document
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
$deployment = $this->getDeployment();
|
||||
$rootNode = $this->createSubmissionFileNode($doc, $submissionFile);
|
||||
if (!$rootNode) {
|
||||
return $rootNode;
|
||||
}
|
||||
|
||||
$doc->appendChild($rootNode);
|
||||
$rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$rootNode->setAttribute('xsi:schemaLocation', $deployment->getNamespace() . ' ' . $deployment->getSchemaFilename());
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
//
|
||||
// SubmissionFile conversion functions
|
||||
//
|
||||
/**
|
||||
* Create and return a submissionFile node.
|
||||
*/
|
||||
public function createSubmissionFileNode(DOMDocument $doc, SubmissionFile $submissionFile): ?DOMElement
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
$context = $deployment->getContext();
|
||||
$stageToName = array_flip($deployment->getStageNameStageIdMapping());
|
||||
|
||||
// Quit if the submission file has an invalid file stage
|
||||
if (!isset($stageToName[$submissionFile->getFileStage()])) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getId(), __('plugins.importexport.native.error.submissionFileInvalidFileStage', ['id' => $submissionFile->getId()]));
|
||||
return null;
|
||||
}
|
||||
|
||||
$genreDao = DAORegistry::getDAO('GenreDAO'); /** @var GenreDAO $genreDao */
|
||||
$genre = $genreDao->getById($submissionFile->getData('genreId'));
|
||||
$uploaderUser = Repo::user()->get($submissionFile->getData('uploaderUserId'), true);
|
||||
|
||||
// Create the submission_file node and set metadata
|
||||
$submissionFileNode = $doc->createElementNS($deployment->getNamespace(), $this->getSubmissionFileElementName());
|
||||
$submissionFileNode->setAttribute('id', $submissionFile->getId());
|
||||
$submissionFileNode->setAttribute('created_at', date('Y-m-d', strtotime($submissionFile->getData('createdAt'))));
|
||||
$submissionFileNode->setAttribute('date_created', $submissionFile->getData('dateCreated'));
|
||||
$submissionFileNode->setAttribute('file_id', $submissionFile->getData('fileId'));
|
||||
$submissionFileNode->setAttribute('stage', $stageToName[$submissionFile->getFileStage()]);
|
||||
$submissionFileNode->setAttribute('updated_at', date('Y-m-d', strtotime($submissionFile->getData('updatedAt'))));
|
||||
$submissionFileNode->setAttribute('viewable', $submissionFile->getViewable() ? 'true' : 'false');
|
||||
if ($caption = $submissionFile->getData('caption')) {
|
||||
$submissionFileNode->setAttribute('caption', $caption);
|
||||
}
|
||||
if ($copyrightOwner = $submissionFile->getData('copyrightOwner')) {
|
||||
$submissionFileNode->setAttribute('copyright_owner', $copyrightOwner);
|
||||
}
|
||||
if ($credit = $submissionFile->getData('credit')) {
|
||||
$submissionFileNode->setAttribute('credit', $credit);
|
||||
}
|
||||
if ($submissionFile->getData('directSalesPrice') != null) {
|
||||
$submissionFileNode->setAttribute('direct_sales_price', $submissionFile->getData('directSalesPrice'));
|
||||
}
|
||||
if ($genre) {
|
||||
$submissionFileNode->setAttribute('genre', $genre->getName($context->getPrimaryLocale()));
|
||||
}
|
||||
if ($language = $submissionFile->getData('language')) {
|
||||
$submissionFileNode->setAttribute('language', $language);
|
||||
}
|
||||
if ($salesType = $submissionFile->getData('salesType')) {
|
||||
$submissionFileNode->setAttribute('sales_type', $salesType);
|
||||
}
|
||||
if ($sourceSubmissionFileId = $submissionFile->getData('sourceSubmissionFileId')) {
|
||||
$submissionFileNode->setAttribute('source_submission_file_id', $sourceSubmissionFileId);
|
||||
}
|
||||
if ($terms = $submissionFile->getData('terms')) {
|
||||
$submissionFileNode->setAttribute('terms', $terms);
|
||||
}
|
||||
if ($uploaderUser) {
|
||||
$submissionFileNode->setAttribute('uploader', $uploaderUser->getUsername());
|
||||
}
|
||||
|
||||
// Add pub-id plugins
|
||||
$this->addIdentifiers($doc, $submissionFileNode, $submissionFile);
|
||||
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'creator', $submissionFile->getData('creator'));
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'description', $submissionFile->getData('description'));
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'name', $submissionFile->getData('name'));
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'publisher', $submissionFile->getData('publisher'));
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'source', $submissionFile->getData('source'));
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'sponsor', $submissionFile->getData('sponsor'));
|
||||
$this->createLocalizedNodes($doc, $submissionFileNode, 'subject', $submissionFile->getData('subject'));
|
||||
|
||||
// If it is a dependent file, add submission_file_ref element
|
||||
if ($submissionFile->getData('fileStage') == SubmissionFile::SUBMISSION_FILE_DEPENDENT && $submissionFile->getData('assocType') == PKPApplication::ASSOC_TYPE_SUBMISSION_FILE) {
|
||||
$fileRefNode = $doc->createElementNS($deployment->getNamespace(), 'submission_file_ref');
|
||||
$fileRefNode->setAttribute('id', $submissionFile->getData('assocId'));
|
||||
$submissionFileNode->appendChild($fileRefNode);
|
||||
}
|
||||
|
||||
// Create the revision nodes
|
||||
$revisions = Repo::submissionFile()->getRevisions(($submissionFile->getId()));
|
||||
$basePath = rtrim(Config::getVar('files', 'files_dir'), '/') . '/';
|
||||
$hasValidRevision = false;
|
||||
foreach ($revisions as $revision) {
|
||||
$localPath = $basePath . $revision->path;
|
||||
if (!file_exists($localPath)) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getId(), __('plugins.importexport.native.error.submissionFileRevisionMissing', ['id' => $submissionFile->getId(), 'revision' => $revision->revision_id, 'path' => $localPath]));
|
||||
continue;
|
||||
}
|
||||
$hasValidRevision = true;
|
||||
|
||||
$revisionNode = $doc->createElementNS($deployment->getNamespace(), 'file');
|
||||
$revisionNode->setAttribute('id', $revision->fileId);
|
||||
$revisionNode->setAttribute('filesize', filesize($localPath));
|
||||
$revisionNode->setAttribute('extension', pathinfo($revision->path, PATHINFO_EXTENSION));
|
||||
$submissionFileNode->appendChild($revisionNode);
|
||||
|
||||
if (!($this->opts['no-embed'] ?? false)) {
|
||||
$embedNode = $doc->createElementNS($deployment->getNamespace(), 'embed', base64_encode(file_get_contents($localPath)));
|
||||
$embedNode->setAttribute('encoding', 'base64');
|
||||
$revisionNode->appendChild($embedNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
$hrefNode = $doc->createElementNS($deployment->getNamespace(), 'href');
|
||||
$revisionNode->appendChild($hrefNode);
|
||||
$hrefNode->setAttribute('mime_type', $revision->mimetype);
|
||||
|
||||
if (!($this->opts['use-file-urls'] ?? false)) {
|
||||
$hrefNode->setAttribute('src', $revision->path);
|
||||
continue;
|
||||
}
|
||||
|
||||
$baseParams ??= [
|
||||
'submissionFileId' => $submissionFile->getId(),
|
||||
'submissionId' => $submissionFile->getData('submissionId'),
|
||||
'stageId' => Repo::submissionFile()->getWorkflowStageId($submissionFile),
|
||||
];
|
||||
$params = $baseParams + ['fileId' => $revision->fileId];
|
||||
$dispatcher ??= Application::get()->getDispatcher();
|
||||
$request ??= Application::get()->getRequest();
|
||||
$url = $dispatcher->url($request, PKPApplication::ROUTE_COMPONENT, $context->getPath(), 'api.file.FileApiHandler', 'downloadFile', null, $params);
|
||||
$hrefNode->setAttribute('src', $url);
|
||||
}
|
||||
|
||||
// Report if no revision has been added
|
||||
if (!$hasValidRevision) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, $submissionFile->getId(), __('plugins.importexport.native.error.submissionFileWithoutRevision', ['id' => $submissionFile->getId()]));
|
||||
return null;
|
||||
}
|
||||
|
||||
return $submissionFileNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and add identifier nodes to a submission node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $revisionNode
|
||||
* @param SubmissionFile $submissionFile
|
||||
*/
|
||||
public function addIdentifiers($doc, $revisionNode, $submissionFile)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
// Ommiting the internal ID here because it is in the submission_file attribute
|
||||
|
||||
// Add public ID
|
||||
if ($pubId = $submissionFile->getStoredPubId('publisher-id')) {
|
||||
$revisionNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', htmlspecialchars($pubId, ENT_COMPAT, 'UTF-8')));
|
||||
$node->setAttribute('type', 'public');
|
||||
$node->setAttribute('advice', 'update');
|
||||
}
|
||||
|
||||
// Add pub IDs by plugin
|
||||
$pubIdPlugins = PluginRegistry::loadCategory('pubIds', true, $deployment->getContext()->getId());
|
||||
foreach ($pubIdPlugins as $pubIdPlugin) {
|
||||
$this->addPubIdentifier($doc, $revisionNode, $submissionFile, $pubIdPlugin->getPubIdType());
|
||||
}
|
||||
// Also add DOI
|
||||
$this->addPubIdentifier($doc, $revisionNode, $submissionFile, 'doi');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single pub ID element for a given plugin to the document.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $revisionNode
|
||||
* @param SubmissionFile $submissionFile
|
||||
*
|
||||
* @return ?\DOMElement
|
||||
*/
|
||||
public function addPubIdentifier($doc, $revisionNode, $submissionFile, $pubIdType)
|
||||
{
|
||||
$pubId = $submissionFile->getStoredPubId($pubIdType);
|
||||
if ($pubId) {
|
||||
$deployment = $this->getDeployment();
|
||||
$revisionNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', htmlspecialchars($pubId, ENT_COMPAT, 'UTF-8')));
|
||||
$node->setAttribute('type', $pubIdType);
|
||||
$node->setAttribute('advice', 'update');
|
||||
return $node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the submission file element name
|
||||
*/
|
||||
public function getSubmissionFileElementName()
|
||||
{
|
||||
return 'submission_file';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file plugins/importexport/native/filter/SubmissionNativeXmlFilter.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 SubmissionNativeXmlFilter
|
||||
*
|
||||
* @ingroup plugins_importexport_native
|
||||
*
|
||||
* @brief Base class that converts a set of submissions to a Native XML document
|
||||
*/
|
||||
|
||||
namespace PKP\plugins\importexport\native\filter;
|
||||
|
||||
use APP\facades\Repo;
|
||||
use APP\submission\Submission;
|
||||
use DOMDocument;
|
||||
use DOMElement;
|
||||
use PKP\core\PKPApplication;
|
||||
use PKP\db\DAORegistry;
|
||||
use PKP\filter\FilterGroup;
|
||||
use PKP\plugins\importexport\PKPImportExportFilter;
|
||||
use PKP\submissionFile\SubmissionFile;
|
||||
use PKP\workflow\WorkflowStageDAO;
|
||||
|
||||
class SubmissionNativeXmlFilter extends NativeExportFilter
|
||||
{
|
||||
public $_includeSubmissionsNode;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param FilterGroup $filterGroup
|
||||
*/
|
||||
public function __construct($filterGroup)
|
||||
{
|
||||
$this->setDisplayName('Native XML submission export');
|
||||
parent::__construct($filterGroup);
|
||||
}
|
||||
|
||||
//
|
||||
// Implement template methods from Filter
|
||||
//
|
||||
/**
|
||||
* @see Filter::process()
|
||||
*
|
||||
* @param array $submissions Array of submissions
|
||||
*
|
||||
* @return DOMDocument
|
||||
*/
|
||||
public function &process(&$submissions)
|
||||
{
|
||||
// Create the XML document
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
if (count($submissions) == 1 && !$this->getIncludeSubmissionsNode()) {
|
||||
// Only one submission specified; create root node
|
||||
$rootNode = $this->createSubmissionNode($doc, $submissions[0]);
|
||||
} else {
|
||||
// Multiple submissions; wrap in a <submissions> element
|
||||
$rootNode = $doc->createElementNS($deployment->getNamespace(), $deployment->getSubmissionsNodeName());
|
||||
foreach ($submissions as $submission) {
|
||||
$rootNode->appendChild($this->createSubmissionNode($doc, $submission));
|
||||
}
|
||||
}
|
||||
$doc->appendChild($rootNode);
|
||||
$rootNode->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
|
||||
$rootNode->setAttribute('xsi:schemaLocation', $deployment->getNamespace() . ' ' . $deployment->getSchemaFilename());
|
||||
|
||||
return $doc;
|
||||
}
|
||||
|
||||
//
|
||||
// Submission conversion functions
|
||||
//
|
||||
/**
|
||||
* Create and return a submission node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param Submission $submission
|
||||
*
|
||||
* @return \DOMElement
|
||||
*/
|
||||
public function createSubmissionNode($doc, $submission)
|
||||
{
|
||||
// Create the root node and attributes
|
||||
$deployment = $this->getDeployment();
|
||||
$deployment->setSubmission($submission);
|
||||
$submissionNode = $doc->createElementNS($deployment->getNamespace(), $deployment->getSubmissionNodeName());
|
||||
|
||||
$submissionNode->setAttribute('locale', $submission->getData('locale'));
|
||||
$submissionNode->setAttribute('date_submitted', date('Y-m-d', strtotime($submission->getData('dateSubmitted'))));
|
||||
$submissionNode->setAttribute('status', $submission->getData('status'));
|
||||
$submissionNode->setAttribute('submission_progress', $submission->getData('submissionProgress'));
|
||||
$submissionNode->setAttribute('current_publication_id', $submission->getData('currentPublicationId'));
|
||||
|
||||
$workflowStageDao = DAORegistry::getDAO('WorkflowStageDAO'); /** @var WorkflowStageDAO $workflowStageDao */
|
||||
$submissionNode->setAttribute('stage', WorkflowStageDAO::getPathFromId($submission->getData('stageId')));
|
||||
|
||||
$this->addIdentifiers($doc, $submissionNode, $submission);
|
||||
$this->addFiles($doc, $submissionNode, $submission);
|
||||
$this->addPublications($doc, $submissionNode, $submission);
|
||||
|
||||
return $submissionNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and add identifier nodes to a submission node.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $submissionNode
|
||||
* @param Submission $submission
|
||||
*/
|
||||
public function addIdentifiers($doc, $submissionNode, $submission)
|
||||
{
|
||||
$deployment = $this->getDeployment();
|
||||
|
||||
// Add internal ID
|
||||
$submissionNode->appendChild($node = $doc->createElementNS($deployment->getNamespace(), 'id', $submission->getId()));
|
||||
$node->setAttribute('type', 'internal');
|
||||
$node->setAttribute('advice', 'ignore');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the submission files to its DOM element.
|
||||
*/
|
||||
public function addFiles(DOMDocument $doc, DOMElement $submissionNode, Submission $submission): void
|
||||
{
|
||||
$submissionFiles = Repo::submissionFile()
|
||||
->getCollector()
|
||||
->filterBySubmissionIds([$submission->getId()])
|
||||
->includeDependentFiles()
|
||||
->getMany();
|
||||
|
||||
$deployment = $this->getDeployment();
|
||||
foreach ($submissionFiles as $submissionFile) {
|
||||
// Skip files attached to objects that are not included in the export,
|
||||
// such as files uploaded to discussions and files uploaded by reviewers
|
||||
$excludedFileStages = [
|
||||
SubmissionFile::SUBMISSION_FILE_QUERY,
|
||||
SubmissionFile::SUBMISSION_FILE_NOTE,
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT,
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_FILE,
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_ATTACHMENT,
|
||||
SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION,
|
||||
SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_FILE,
|
||||
SubmissionFile::SUBMISSION_FILE_INTERNAL_REVIEW_REVISION
|
||||
];
|
||||
|
||||
if (in_array($submissionFile->getData('fileStage'), $excludedFileStages)) {
|
||||
$deployment->addWarning(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.native.error.submissionFileSkipped', ['id' => $submissionFile->getId()]));
|
||||
continue;
|
||||
}
|
||||
$currentFilter = PKPImportExportFilter::getFilter('SubmissionFile=>native-xml', $this->getDeployment(), $this->opts);
|
||||
$submissionFileDoc = $currentFilter->execute($submissionFile, true);
|
||||
if ($submissionFileDoc) {
|
||||
$clone = $doc->importNode($submissionFileDoc->documentElement, true);
|
||||
$submissionNode->appendChild($clone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the submission files to its DOM element.
|
||||
*
|
||||
* @param \DOMDocument $doc
|
||||
* @param \DOMElement $submissionNode
|
||||
* @param Submission $submission
|
||||
*/
|
||||
public function addPublications($doc, $submissionNode, $submission)
|
||||
{
|
||||
$currentFilter = PKPImportExportFilter::getFilter('publication=>native-xml', $this->getDeployment());
|
||||
|
||||
$publications = $submission->getData('publications');
|
||||
foreach ($publications as $publication) {
|
||||
$publicationDoc = $currentFilter->execute($publication);
|
||||
|
||||
if ($publicationDoc && $publicationDoc->documentElement instanceof DOMElement) {
|
||||
$clone = $doc->importNode($publicationDoc->documentElement, true);
|
||||
$submissionNode->appendChild($clone);
|
||||
} else {
|
||||
$deployment = $this->getDeployment();
|
||||
$deployment->addError(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId(), __('plugins.importexport.publication.exportFailed'));
|
||||
|
||||
throw new \Exception(__('plugins.importexport.publication.exportFailed'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Abstract methods for subclasses to implement
|
||||
//
|
||||
|
||||
/**
|
||||
* Sets a flag to always include the <submissions> node, even if there
|
||||
* may only be one submission.
|
||||
*
|
||||
* @param bool $includeSubmissionsNode
|
||||
*/
|
||||
public function setIncludeSubmissionsNode($includeSubmissionsNode)
|
||||
{
|
||||
$this->_includeSubmissionsNode = $includeSubmissionsNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether to always include the <submissions> node, even if there
|
||||
* may only be one submission.
|
||||
*
|
||||
* @return bool $includeSubmissionsNode
|
||||
*/
|
||||
public function getIncludeSubmissionsNode()
|
||||
{
|
||||
return $this->_includeSubmissionsNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!--
|
||||
* plugins/importexport/native/pkp-native.xsd
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Schema describing native XML import/export elements shared across PKP applications
|
||||
-->
|
||||
|
||||
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://pkp.sfu.ca" xmlns:pkp="http://pkp.sfu.ca" elementFormDefault="qualified">
|
||||
|
||||
<!-- Bring in the common PKP import/export content -->
|
||||
<include schemaLocation="../../../xml/importexport.xsd" />
|
||||
|
||||
<!-- *********
|
||||
- * Types *
|
||||
- ********* -->
|
||||
|
||||
<!--
|
||||
- Basic Types
|
||||
-->
|
||||
|
||||
<!-- Identifies a MIME type -->
|
||||
<simpleType name="mimeType">
|
||||
<restriction base="normalizedString" />
|
||||
</simpleType>
|
||||
|
||||
<!-- Identifies a user group -->
|
||||
<simpleType name="user_group_ref">
|
||||
<restriction base="normalizedString" />
|
||||
</simpleType>
|
||||
|
||||
<!-- Identifies a user name -->
|
||||
<simpleType name="username">
|
||||
<restriction base="normalizedString" />
|
||||
</simpleType>
|
||||
|
||||
|
||||
<!--
|
||||
- File-related Types
|
||||
-->
|
||||
|
||||
<!-- Describes a filename -->
|
||||
<simpleType name="filename">
|
||||
<restriction base="normalizedString">
|
||||
</restriction>
|
||||
</simpleType>
|
||||
|
||||
<!-- A remote tag defining an remotely hosted representation -->
|
||||
<complexType name="remote">
|
||||
<attribute name="src" type="anyURI" />
|
||||
</complexType>
|
||||
|
||||
<!-- An href tag defining an external URL file resource -->
|
||||
<complexType name="href">
|
||||
<attribute name="src" type="anyURI" />
|
||||
<attribute name="mime_type" type="pkp:mimeType" />
|
||||
</complexType>
|
||||
|
||||
<!-- An embed tag defining an encoded, embedded file resource -->
|
||||
<complexType name="embed" mixed="true">
|
||||
<attribute name="encoding" use="required">
|
||||
<simpleType>
|
||||
<restriction base="string">
|
||||
<enumeration value="base64" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</attribute>
|
||||
<attribute name="mime_type" type="pkp:mimeType" />
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
- Encapsulate a file, which can be imported from an href,
|
||||
- or embedded directly in the XML document.
|
||||
-->
|
||||
<group name="fileContents">
|
||||
<choice>
|
||||
<element name="href" type="pkp:href" />
|
||||
<element name="embed" type="pkp:embed" />
|
||||
</choice>
|
||||
</group>
|
||||
|
||||
<complexType name="submission_file">
|
||||
<sequence>
|
||||
<element ref="pkp:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="creator" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="description" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="name" type="pkp:localizedNode" minOccurs="1" maxOccurs="unbounded" />
|
||||
<element name="publisher" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="source" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="sponsor" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="subject" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="submission_file_ref" type="pkp:submission_file_ref" minOccurs="0" maxOccurs="1" />
|
||||
<element name="file" minOccurs="1" maxOccurs="unbounded">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<group ref="pkp:fileContents" />
|
||||
</sequence>
|
||||
<attribute name="id" type="int" />
|
||||
<attribute name="filesize" type="int" />
|
||||
<attribute name="extension" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
</sequence>
|
||||
<attribute name="caption" type="string" />
|
||||
<attribute name="copyright_owner" type="string" />
|
||||
<attribute name="created_at" type="date" />
|
||||
<attribute name="credit" type="string" />
|
||||
<attribute name="date_created" type="string" />
|
||||
<attribute name="direct_sales_price" type="decimal" />
|
||||
<attribute name="file_id" type="int" />
|
||||
<attribute name="genre" type="normalizedString" />
|
||||
<attribute name="id" type="int" />
|
||||
<attribute name="language" type="string" />
|
||||
<attribute name="sales_type" type="string" />
|
||||
<attribute name="source_submission_file_id" type="int" />
|
||||
<attribute name="stage">
|
||||
<simpleType>
|
||||
<restriction base="string">
|
||||
<enumeration value="public" />
|
||||
<enumeration value="submission" />
|
||||
<enumeration value="note" />
|
||||
<enumeration value="review_file" />
|
||||
<enumeration value="review_attachment" />
|
||||
<enumeration value="final" />
|
||||
<enumeration value="fair_copy" />
|
||||
<enumeration value="editor" />
|
||||
<enumeration value="copyedit" />
|
||||
<enumeration value="proof" />
|
||||
<enumeration value="production_ready" />
|
||||
<enumeration value="attachment" />
|
||||
<enumeration value="query" />
|
||||
<enumeration value="review_revision" />
|
||||
<enumeration value="dependent" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</attribute>
|
||||
<attribute name="terms" type="string" />
|
||||
<attribute name="updated_at" type="date" />
|
||||
<attribute name="uploader" type="pkp:username" />
|
||||
<attribute name="viewable" type="boolean" />
|
||||
</complexType>
|
||||
|
||||
<!-- A reference to a submission file that is declared elsewhere -->
|
||||
<complexType name="submission_file_ref">
|
||||
<attribute name="id" type="int" />
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
- User-related Elements
|
||||
-->
|
||||
|
||||
<!-- A user group -->
|
||||
<complexType name="user_group">
|
||||
<sequence>
|
||||
<element name="role_id" type="int" />
|
||||
<element name="context_id" type="int" />
|
||||
<element name="is_default" type="boolean" />
|
||||
<element name="show_title" type="boolean" />
|
||||
<element name="permit_self_registration" type="boolean" />
|
||||
<element name="permit_metadata_edit" type="boolean" />
|
||||
<element name="name" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="abbrev" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="stage_assignments" type="string" minOccurs="1" maxOccurs="1" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- Permit "user_group" as a root element -->
|
||||
<element name="user_group" type="pkp:user_group" />
|
||||
|
||||
<!-- A user group -->
|
||||
<complexType name="user_groups">
|
||||
<sequence>
|
||||
<element name="user_group" type="pkp:user_group" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<!-- Permit "user_groups" as a root element -->
|
||||
<element name="user_groups" type="pkp:user_groups" />
|
||||
|
||||
<!-- An identity (e.g. user, author) -->
|
||||
<complexType name="identity" abstract="true">
|
||||
<sequence>
|
||||
<element name="givenname" type="pkp:localizedNode" minOccurs="1" maxOccurs="unbounded" />
|
||||
<element name="familyname" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="affiliation" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="country" type="string" minOccurs="0" maxOccurs="1" />
|
||||
<element name="email" type="string" />
|
||||
<element name="url" type="anyURI" minOccurs="0" maxOccurs="1" />
|
||||
<element name="orcid" type="anyURI" minOccurs="0" maxOccurs="1" />
|
||||
<element name="biography" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
|
||||
<complexType name="author">
|
||||
<complexContent>
|
||||
<extension base="pkp:identity">
|
||||
<attribute name="primary_contact" type="boolean" default="false" />
|
||||
<attribute name="user_group_ref" type="pkp:user_group_ref" use="required" />
|
||||
<attribute name="include_in_browse" type="boolean" default="true" />
|
||||
<attribute name="seq" type="int" use="required" />
|
||||
<attribute name="id" type="int" use="required" />
|
||||
</extension>
|
||||
</complexContent>
|
||||
</complexType>
|
||||
|
||||
<!--
|
||||
- Representation-related types
|
||||
-->
|
||||
|
||||
<complexType name="representation">
|
||||
<sequence>
|
||||
<element ref="pkp:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="name" type="pkp:localizedNode" minOccurs="1" maxOccurs="unbounded" />
|
||||
<element name="seq" type="int" minOccurs="1" maxOccurs="1" />
|
||||
<choice>
|
||||
<element name="submission_file_ref" type="pkp:submission_file_ref" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="remote" type="pkp:remote" minOccurs="0" maxOccurs="1" />
|
||||
</choice>
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" use="optional" />
|
||||
<attribute name="url_path" type="string" use="optional" />
|
||||
</complexType>
|
||||
|
||||
<complexType name="submission">
|
||||
<sequence>
|
||||
<element ref="pkp:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
||||
<!-- Metadata -->
|
||||
<element ref="pkp:submission_file" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:pkppublication" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="status" type="string" use="optional" />
|
||||
<attribute name="current_publication_id" type="int" use="optional" />
|
||||
<attribute name="date_submitted" type="date" use="optional" />
|
||||
<attribute name="submission_progress" type="string" use="optional" />
|
||||
<attribute name="locale" type="string" use="optional" />
|
||||
</complexType>
|
||||
|
||||
<complexType name="pkppublication">
|
||||
<sequence>
|
||||
<element ref="pkp:id" minOccurs="0" maxOccurs="unbounded" />
|
||||
|
||||
<!-- Metadata -->
|
||||
<element ref="pkp:title" minOccurs="1" maxOccurs="unbounded" />
|
||||
<element name="prefix" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="subtitle" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="abstract" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="coverage" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="type" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="source" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="rights" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="licenseUrl" type="anyURI" minOccurs="0" maxOccurs="1" />
|
||||
<element name="copyrightHolder" type="pkp:localizedNode" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element name="copyrightYear" type="int" minOccurs="0" maxOccurs="1" />
|
||||
<element ref="pkp:keywords" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:agencies" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:languages" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:disciplines" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:subjects" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:authors" minOccurs="0" maxOccurs="1" />
|
||||
<element ref="pkp:representation" minOccurs="0" maxOccurs="unbounded" />
|
||||
<element ref="pkp:citations" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</sequence>
|
||||
<attribute name="date_submitted" type="date" use="optional" />
|
||||
<attribute name="date_published" type="date" use="optional" />
|
||||
<attribute name="version" type="int" use="optional" />
|
||||
<attribute name="status" type="int" use="optional" />
|
||||
<attribute name="primary_contact_id" type="int" use="optional" />
|
||||
<attribute name="url_path" type="string" use="optional" />
|
||||
</complexType>
|
||||
|
||||
<!-- ************
|
||||
- * Elements *
|
||||
- ************ -->
|
||||
|
||||
<!--
|
||||
- Identifier elements
|
||||
-->
|
||||
<element name="id">
|
||||
<complexType mixed="true">
|
||||
<attribute name="type" type="string" use="optional" />
|
||||
<attribute name="advice" default="ignore">
|
||||
<simpleType>
|
||||
<restriction base="string">
|
||||
<enumeration value="update" />
|
||||
<enumeration value="ignore" />
|
||||
</restriction>
|
||||
</simpleType>
|
||||
</attribute>
|
||||
</complexType>
|
||||
</element>
|
||||
|
||||
<!--
|
||||
- Metadata element types
|
||||
-->
|
||||
<element name="title" type="pkp:localizedNode" />
|
||||
|
||||
<!--
|
||||
- Composite / root elements
|
||||
-->
|
||||
<!-- Permit "submissions" as a root element -->
|
||||
<element name="submissions" abstract="true">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element ref="pkp:submission" minOccurs="0" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
|
||||
<!-- Permit "authors" as a root element to keep the filters happy -->
|
||||
<element name="authors">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="author" type="pkp:author" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
</complexType>
|
||||
</element>
|
||||
|
||||
<!-- Permit "author" as a root element to keep the filters happy -->
|
||||
<element name="author" type="pkp:author" />
|
||||
|
||||
<!--
|
||||
- Representation-related elements
|
||||
-->
|
||||
<!-- Permit "representation" as a root element to keep the filters happy -->
|
||||
<element name="representation" type="pkp:representation" abstract="true" />
|
||||
<element name="submission_file" type="pkp:submission_file" />
|
||||
|
||||
<!--
|
||||
- Submission-related elements
|
||||
-->
|
||||
<!-- Permit "submission" as a root element -->
|
||||
<element name="submission" type="pkp:submission" abstract="true" />
|
||||
|
||||
<element name="pkppublication" type="pkp:pkppublication" />
|
||||
|
||||
<!-- Controlled vocabularies -->
|
||||
<element name="keywords">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="keyword" type="string" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="agencies">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="agency" type="string" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="languages">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="language" type="string" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="disciplines">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="discipline" type="string" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="subjects">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="subject" type="string" minOccurs="1" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
<element name="citations">
|
||||
<complexType>
|
||||
<sequence>
|
||||
<element name="citation" type="string" minOccurs="0" maxOccurs="unbounded" />
|
||||
</sequence>
|
||||
<attribute name="locale" type="string" />
|
||||
</complexType>
|
||||
</element>
|
||||
</schema>
|
||||
Reference in New Issue
Block a user