%PDF-1.7 GIF89;
| Server IP : 104.20.45.2 / Your IP : 172.16.20.3 Web Server : Apache/2.4.25 (Debian) System : Linux f64a392e70de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 User : application ( 1000) PHP Version : 5.6.40 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /app/plugins/importexport/crossref/ |
Upload File : |
<?php
/**
* @file plugins/importexport/crossref/CrossRefExportPlugin.inc.php
*
* Copyright (c) 2013-2019 Simon Fraser University
* Copyright (c) 2003-2019 John Willinsky
* Distributed under the GNU GPL v2. For full terms see the file docs/COPYING.
*
* @class CrossRefExportPlugin
* @ingroup plugins_importexport_crossref
*
* @brief CrossRef export/registration plugin.
*/
if (!class_exists('DOIExportPlugin')) { // Bug #7848
import('plugins.importexport.crossref.classes.DOIExportPlugin');
}
define('CROSSREF_STATUS_SUBMITTED', 'submitted');
define('CROSSREF_STATUS_COMPLETED', 'completed');
define('CROSSREF_STATUS_FAILED', 'failed');
define('CROSSREF_STATUS_REGISTERED', 'found');
define('CROSSREF_STATUS_MARKEDREGISTERED', 'markedRegistered');
define('CROSSREF_STATUS_NOT_DEPOSITED', 'notDeposited');
// DataCite API
define('CROSSREF_API_DEPOSIT_OK', 303);
define('CROSSREF_API_RESPONSE_OK', 200);
define('CROSSREF_API_URL', 'https://api.crossref.org/deposits');
//TESTING
//define('CROSSREF_API_URL', 'https://api.crossref.org/deposits?test=true');
define('CROSSREF_SEARCH_API', 'http://search.crossref.org/dois');
define('CROSSREF_WORKS_API', 'http://api.crossref.org/works/');
// The name of the settings used to save the registered DOI and the URL with the deposit status.
define('CROSSREF_DEPOSIT_STATUS', 'depositStatus');
class CrossRefExportPlugin extends DOIExportPlugin {
//
// Constructor
//
function CrossRefExportPlugin() {
parent::DOIExportPlugin();
}
//
// Implement template methods from ImportExportPlugin
//
/**
* @see ImportExportPlugin::getName()
*/
function getName() {
return 'CrossRefExportPlugin';
}
/**
* @see ImportExportPlugin::getDisplayName()
*/
function getDisplayName() {
return __('plugins.importexport.crossref.displayName');
}
/**
* @see ImportExportPlugin::getDescription()
*/
function getDescription() {
return __('plugins.importexport.crossref.description');
}
/**
* @see LazyLoadPlugin::register()
*/
function register($category, $path) {
$success = parent::register($category, $path);
if (!Config::getVar('general', 'installed')) return false;
if ($success) {
HookRegistry::register('AcronPlugin::parseCronTab', array($this, 'callbackParseCronTab'));
}
return $success;
}
//
// Implement template methods from DOIExportPlugin
//
/**
* @see DOIExportPlugin::getPluginId()
*/
function getPluginId() {
return 'crossref';
}
/**
* @see DOIExportPlugin::getSettingsFormClassName()
*/
function getSettingsFormClassName() {
return 'CrossRefSettingsForm';
}
/**
* @see DOIExportPlugin::getAllObjectTypes()
*/
function getAllObjectTypes() {
return array(
'issue' => DOI_EXPORT_ISSUES,
'article' => DOI_EXPORT_ARTICLES
);
}
/**
* Process a DOI activity request.
* @param $request PKPRequest
* @param $journal Journal
*/
function process(&$request, &$journal) {
if ($request->getUserVar('checkStatus')) {
// Update status is awailable only for articles
$articleIds = (array) $request->getUserVar('articleId');
$articles =& $this->_getObjectsFromIds(DOI_EXPORT_ARTICLES, $articleIds, $journal->getId(), $errors);
foreach ($articles as $article) {
$this->updateDepositStatus($request, $journal, $article);
}
$request->redirect(
null, null, null,
array('plugin', $this->getName(), 'articles'),
null
);
} else {
parent::process($request, $journal);
}
}
/**
* Display a list of issues for export.
* @param $templateMgr TemplateManager
* @param $journal Journal
*/
function displayIssueList(&$templateMgr, &$journal) {
$this->setBreadcrumbs(array(), true);
// Retrieve all published issues.
AppLocale::requireComponents(array(LOCALE_COMPONENT_OJS_EDITOR));
$issueDao =& DAORegistry::getDAO('IssueDAO'); /* @var $issueDao IssueDAO */
$this->registerDaoHook('IssueDAO');
$issueIterator =& $issueDao->getPublishedIssues($journal->getId(), Handler::getRangeInfo('issues'));
// Get issues that should be excluded i.e. that have no objects eligible to export/register.
$publishedArticleDao =& DAORegistry::getDAO('PublishedArticleDAO');
$excludes = array();
$allExcluded = true;
$numArticles = array();
while ($issue =& $issueIterator->next()) {
$excludes[$issue->getId()] = true;
$issueArticles =& $publishedArticleDao->getPublishedArticles($issue->getId());
$issueArticlesNo = 0;
$allArticlesRegistered[$issue->getId()] = true;
foreach ($issueArticles as $issueArticle) {
$articleRegistered = $issueArticle->getData($this->getPluginId().'::registeredDoi');
$errors = array();
if ($this->canBeExported($issueArticle, $errors)) {
$excludes[$issue->getId()] = false;
$allExcluded = false;
$issueArticlesNo++;
}
if ($allArticlesRegistered[$issue->getId()] && !isset($articleRegistered)) {
$allArticlesRegistered[$issue->getId()] = false;
}
}
$numArticles[$issue->getId()] = $issueArticlesNo;
unset($issue);
}
unset($issueIterator);
// Prepare and display the issue template.
// Get the issue iterator from the DB for the template again.
$issueIterator =& $issueDao->getPublishedIssues($journal->getId(), Handler::getRangeInfo('issues'));
$templateMgr->assign_by_ref('issues', $issueIterator);
$templateMgr->assign('allExcluded', $allExcluded);
$templateMgr->assign('excludes', $excludes);
$templateMgr->assign('numArticles', $numArticles);
$templateMgr->assign('allArticlesRegistered', $allArticlesRegistered);
$templateMgr->display($this->getTemplatePath() . 'issues.tpl');
}
/**
* @copydoc DOIExportPlugin::displayArticleList
*/
function displayArticleList(&$templateMgr, &$journal) {
// Prepare information specific to this plug-in.
$this->setBreadcrumbs(array(), true);
$filter = $templateMgr->get_template_vars('filter');
// Retrieve all published articles.
$this->registerDaoHook('PublishedArticleDAO');
$publishedArticleDao =& DAORegistry::getDAO('PublishedArticleDAO'); /* @var $publishedArticleDao PublishedArticleDAO */
$articles = array();
if ($filter) {
$publishedArticleDao =& DAORegistry::getDAO('PublishedArticleDAO'); /* @var $publishedArticleDao PublishedArticleDAO */
if ($filter == CROSSREF_STATUS_NOT_DEPOSITED) {
$allArticles = $publishedArticleDao->getBySetting($this->getDepositStatusSettingName(), null, $journal->getId());
} else {
$allArticles = $publishedArticleDao->getBySetting($this->getDepositStatusSettingName(), $filter, $journal->getId());
}
} else {
$allArticles = $this->getAllPublishedArticles($journal);
}
// Retrieve article data.
$articleData = array();
$errors = array();
foreach($allArticles as $article) {
if ($this->canBeExported($article, $errors)) {
$preparedArticle = $this->_prepareArticleData($article, $journal);
if (is_array($preparedArticle)) {
$articleData[] = $preparedArticle;
$articles[] = $article;
}
}
unset($article, $preparedArticle);
}
unset($articles);
// Paginate articles.
$totalArticles = count($articleData);
$rangeInfo = Handler::getRangeInfo('articles');
if ($rangeInfo->isValid()) {
$articleData = array_slice($articleData, $rangeInfo->getCount() * ($rangeInfo->getPage()-1), $rangeInfo->getCount());
}
// Instantiate article iterator.
import('lib.pkp.classes.core.VirtualArrayIterator');
$iterator = new VirtualArrayIterator($articleData, $totalArticles, $rangeInfo->getPage(), $rangeInfo->getCount());
// Prepare and display the article template.
$templateMgr->assign_by_ref('articles', $iterator);
$templateMgr->assign('depositStatusSettingName', $this->getDepositStatusSettingName());
$templateMgr->assign('depositStatusUrlSettingName', $this->getDepositStatusUrlSettingName());
$templateMgr->assign('statusMapping', $this->getStatusMapping());
$templateMgr->assign('isEditor', Validation::isEditor($journal->getId()));
$templateMgr->display($this->getTemplatePath() . 'articles.tpl');
}
/**
* The selected issue can be exported if it contains an article that has a DOI.
* The selected article can be exported if it has a DOI.
* @param $foundObject Issue|PublishedArticle
* @param $errors array
* @return array|boolean
*/
function canBeExported($foundObject, &$errors) {
if (is_a($foundObject, 'Issue')) {
$publishedArticleDao =& DAORegistry::getDAO('PublishedArticleDAO');
$issueArticles =& $publishedArticleDao->getPublishedArticles($foundObject->getId());
foreach ($issueArticles as $issueArticle) {
// if just one article can be exported, than the issue can be exported
if (parent::canBeExported($issueArticle, $errors)) {
return true;
}
}
}
if (is_a($foundObject, 'PublishedArticle')) {
return parent::canBeExported($foundObject, $errors);
}
return false;
}
/**
* @see DOIExportPlugin::generateExportFiles()
*/
function generateExportFiles(&$request, $exportType, &$objects, $targetPath, &$journal, &$errors) {
// Additional locale file.
AppLocale::requireComponents(array(LOCALE_COMPONENT_OJS_EDITOR));
$this->import('classes.CrossRefExportDom');
$dom = new CrossRefExportDom($request, $this, $journal, $this->getCache());
$doc =& $dom->generate($objects);
if ($doc === false) {
$errors =& $dom->getErrors();
return false;
}
// Write the result to the target file.
$exportFileName = $this->getTargetFileName($targetPath, $exportType);
file_put_contents($exportFileName, XMLCustomWriter::getXML($doc));
$generatedFiles = array($exportFileName => &$objects);
return $generatedFiles;
}
/**
* @see DOIExportPlugin::processMarkRegistered()
*/
function processMarkRegistered(&$request, $exportType, &$objects, &$journal) {
$articleDao =& DAORegistry::getDAO('ArticleDAO'); /* @var $articleDao ArticleDAO */
$this->import('classes.CrossRefExportDom');
$dom = new CrossRefExportDom($request, $this, $journal, $this->getCache());
$statusUpdatePossible = $this->getSetting($journal->getId(), 'username') && $this->getSetting($journal->getId(), 'password');
foreach($objects as $object) {
if (is_a($object, 'Issue')) {
$articlesByIssue =& $dom->retrieveArticlesByIssue($object);
foreach ($articlesByIssue as $article) {
if ($article->getPubId('doi')) {
$articleDao->updateSetting($article->getId(), $this->getDepositStatusSettingName(), CROSSREF_STATUS_MARKEDREGISTERED, 'string');
$this->markRegistered($request, $article);
}
}
} else {
if ($object->getPubId('doi')) {
$articleDao->updateSetting($object->getId(), $this->getDepositStatusSettingName(), CROSSREF_STATUS_MARKEDREGISTERED, 'string');
$this->markRegistered($request, $object);
}
}
}
}
/**
* @see DOIExportPlugin::registerDoi()
*/
function registerDoi(&$request, &$journal, &$objects, $filename) {
$curlCh = curl_init();
if ($httpProxyHost = Config::getVar('proxy', 'http_host')) {
curl_setopt($curlCh, CURLOPT_PROXY, $httpProxyHost);
curl_setopt($curlCh, CURLOPT_PROXYPORT, Config::getVar('proxy', 'http_port', '80'));
if ($username = Config::getVar('proxy', 'username')) {
curl_setopt($curlCh, CURLOPT_PROXYUSERPWD, $username . ':' . Config::getVar('proxy', 'password'));
}
}
curl_setopt($curlCh, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curlCh, CURLOPT_POST, true);
curl_setopt($curlCh, CURLOPT_HEADER, 1);
curl_setopt($curlCh, CURLOPT_BINARYTRANSFER, true);
$username = $this->getSetting($journal->getId(), 'username');
$password = $this->getSetting($journal->getId(), 'password');
curl_setopt($curlCh, CURLOPT_URL, CROSSREF_API_URL);
curl_setopt($curlCh, CURLOPT_USERPWD, "$username:$password");
// Transmit XML data.
assert(is_readable($filename));
$fh = fopen($filename, 'rb');
$httpheaders = array();
$httpheaders[] = 'Content-Type: application/vnd.crossref.deposit+xml';
$httpheaders[] = 'Content-Length: ' . filesize($filename);
curl_setopt($curlCh, CURLOPT_HTTPHEADER, $httpheaders);
curl_setopt($curlCh, CURLOPT_INFILE, $fh);
curl_setopt($curlCh, CURLOPT_INFILESIZE, filesize($filename));
$response = curl_exec($curlCh);
if ($response === false) {
$result = array(array('plugins.importexport.crossref.register.error.mdsError', 'No response from server.'));
} elseif ( $status = curl_getinfo($curlCh, CURLINFO_HTTP_CODE) != CROSSREF_API_DEPOSIT_OK ) {
$result = array(array('plugins.importexport.crossref.register.error.mdsError', "$status - $response"));
} else {
// Deposit was received
$result = true;
$articleDao =& DAORegistry::getDAO('ArticleDAO'); /* @var $articleDao ArticleDAO */
foreach ($objects as $article) {
// its possible that issues, galleys, or other things are being registered
// but we're only going to be going back to check in on articles
if (is_a($article, 'Article')) {
// update the status and save the URL of the last deposit
// (note: the registration could be done outside the system, so it is better to always update the URL together with the status)
$this->updateDepositStatus($request, $journal, $article);
}
}
}
curl_close($curlCh);
return $result;
}
/**
* This method checks the CrossRef APIs, if deposits and registration have been successful
* @param $request Request
* @param $journal Journal The journal associated with the deposit
* @param $article Article The article getting deposited
*/
function updateDepositStatus(&$request, &$journal, $article) {
$articleDao =& DAORegistry::getDAO('ArticleDAO'); /* @var $articleDao ArticleDAO */
import('lib.pkp.classes.core.JSONManager');
$jsonManager = new JSONManager();
// Prepare HTTP session.
$curlCh = curl_init();
if ($httpProxyHost = Config::getVar('proxy', 'http_host')) {
curl_setopt($curlCh, CURLOPT_PROXY, $httpProxyHost);
curl_setopt($curlCh, CURLOPT_PROXYPORT, Config::getVar('proxy', 'http_port', '80'));
if ($username = Config::getVar('proxy', 'username')) {
curl_setopt($curlCh, CURLOPT_PROXYUSERPWD, $username . ':' . Config::getVar('proxy', 'password'));
}
}
curl_setopt($curlCh, CURLOPT_RETURNTRANSFER, true);
$username = $this->getSetting($journal->getId(), 'username');
$password = $this->getSetting($journal->getId(), 'password');
curl_setopt($curlCh, CURLOPT_USERPWD, "$username:$password");
$doi = urlencode($article->getPubId('doi'));
$params = 'filter=doi:' . $doi ;
curl_setopt(
$curlCh,
CURLOPT_URL,
CROSSREF_API_URL . (strpos(CROSSREF_API_URL,'?')===false?'?':'&') . $params
);
// try to fetch from the new API
$response = curl_exec($curlCh);
// try the new API with the filter completed (should only return successes)
if ( $response && curl_getinfo($curlCh, CURLINFO_HTTP_CODE) == CROSSREF_API_RESPONSE_OK ) {
$response = $jsonManager->decode($response);
$pastDeposits = array();
foreach ($response->message->items as $item) {
$pastDeposits[strtotime($item->{'submitted-at'})] = array('status' => $item->status, 'batch-id' => $item->{'batch-id'});
}
// if there have been past attempts, save the most recent one's status for display to user
if (count($pastDeposits) > 0) {
$lastDeposit = $pastDeposits[max(array_keys($pastDeposits))];
$lastStatus = $lastDeposit['status'];
$lastBatchId = $lastDeposit['batch-id'];
// If batch-id changed
if ($article->getData($this->getDepositStatusUrlSettingName()) != '/deposits/'.$lastBatchId) {
// Update the depositStausUrl
$articleDao->updateSetting($article->getId(), $this->getDepositStatusUrlSettingName(), '/deposits/'.$lastBatchId, 'string');
}
if ($lastStatus == CROSSREF_STATUS_COMPLETED) {
// check if the DOI is active (there is a delay between a deposit completing successfully and a DOI being 'ready').
curl_setopt(
$curlCh,
CURLOPT_URL,
CROSSREF_WORKS_API . $doi
);
$response = curl_exec($curlCh);
if ($response && curl_getinfo($curlCh, CURLINFO_HTTP_CODE) == CROSSREF_API_RESPONSE_OK) {
// set the status, because we will need to check it for the automatic registration
$article->setData($this->getDepositStatusSettingName(), CROSSREF_STATUS_REGISTERED);
// Update the status to registered
$articleDao->updateSetting($article->getId(), $this->getDepositStatusSettingName(), CROSSREF_STATUS_REGISTERED, 'string');
$this->markRegistered($request, $article);
return true;
}
}
// If status changed
if ($article->getData($this->getDepositStatusSettingName()) != $lastStatus) {
// set the status, because we will need to check it for the automatic registration
$article->setData($this->getDepositStatusSettingName(), $lastStatus);
// Update the last deposit status
$articleDao->updateSetting($article->getId(), $this->getDepositStatusSettingName(), $lastStatus, 'string');
}
if ($article->getData($this->getPluginId() . '::' . DOI_EXPORT_REGDOI)) {
// apparently there was a new registreation i.e. update
// remove the setting defining the article as registered, for the article to be considered for automatic status updates
$articleDao->updateSetting($article->getId(), $this->getPluginId() . '::' . DOI_EXPORT_REGDOI, null, 'string');
}
}
}
curl_close($curlCh);
return false;
}
/**
* @see AcronPlugin::parseCronTab()
*/
function callbackParseCronTab($hookName, $args) {
$taskFilesPath =& $args[0];
$taskFilesPath[] = $this->getPluginPath() . DIRECTORY_SEPARATOR . 'scheduledTasks.xml';
return false;
}
function getDepositStatusSettingName() {
return $this->getPluginId() . '::' . CROSSREF_DEPOSIT_STATUS;
}
function getDepositStatusUrlSettingName() {
return $this->getPluginId() . '::' . CROSSREF_DEPOSIT_STATUS . 'Url';
}
/**
* Get status mapping for the status display.
* @return array (internal status => string text to be displayed)
*/
function getStatusMapping() {
return array(
CROSSREF_STATUS_SUBMITTED => __('plugins.importexport.crossref.status.submitted'),
CROSSREF_STATUS_COMPLETED => __('plugins.importexport.crossref.status.completed'),
CROSSREF_STATUS_FAILED => __('plugins.importexport.crossref.status.failed'),
CROSSREF_STATUS_REGISTERED => __('plugins.importexport.crossref.status.registered'),
CROSSREF_STATUS_MARKEDREGISTERED => __('plugins.importexport.crossref.status.markedRegistered')
);
}
}
?>