Open Journal Systems  3.3.0
CrossRefExportPlugin.inc.php
1 <?php
2 
16 import('classes.plugins.DOIPubIdExportPlugin');
17 
18 // The status of the Crossref DOI.
19 // any, notDeposited, and markedRegistered are reserved
20 define('CROSSREF_STATUS_FAILED', 'failed');
21 
22 define('CROSSREF_API_DEPOSIT_OK', 200);
23 
24 define('CROSSREF_API_URL', 'https://api.crossref.org/v2/deposits');
25 //TESTING
26 define('CROSSREF_API_URL_DEV', 'https://test.crossref.org/v2/deposits');
27 
28 define('CROSSREF_API_STAUTS_URL', 'https://api.crossref.org/servlet/submissionDownload');
29 //TESTING
30 define('CROSSREF_API_STAUTS_URL_DEV', 'https://test.crossref.org/servlet/submissionDownload');
31 
32 // The name of the setting used to save the registered DOI and the URL with the deposit status.
33 define('CROSSREF_DEPOSIT_STATUS', 'depositStatus');
34 
35 
37 
41  function getName() {
42  return 'CrossRefExportPlugin';
43  }
44 
48  function getDisplayName() {
49  return __('plugins.importexport.crossref.displayName');
50  }
51 
55  function getDescription() {
56  return __('plugins.importexport.crossref.description');
57  }
58 
62  function getSubmissionFilter() {
63  return 'article=>crossref-xml';
64  }
65 
69  function getStatusNames() {
70  return array_merge(parent::getStatusNames(), array(
71  EXPORT_STATUS_REGISTERED => __('plugins.importexport.crossref.status.registered'),
72  CROSSREF_STATUS_FAILED => __('plugins.importexport.crossref.status.failed'),
73  EXPORT_STATUS_MARKEDREGISTERED => __('plugins.importexport.crossref.status.markedRegistered'),
74  ));
75  }
76 
80  function getStatusActions($pubObject) {
81  $request = Application::get()->getRequest();
82  $dispatcher = $request->getDispatcher();
83  return array(
84  CROSSREF_STATUS_FAILED =>
85  new LinkAction(
86  'failureMessage',
87  new AjaxModal(
88  $dispatcher->url(
89  $request, ROUTE_COMPONENT, null,
90  'grid.settings.plugins.settingsPluginGridHandler',
91  'manage', null, array('plugin' => 'CrossRefExportPlugin', 'category' => 'importexport', 'verb' => 'statusMessage',
92  'batchId' => $pubObject->getData($this->getDepositBatchIdSettingName()), 'articleId' => $pubObject->getId())
93  ),
94  __('plugins.importexport.crossref.status.failed'),
95  'failureMessage'
96  ),
97  __('plugins.importexport.crossref.status.failed')
98  )
99  );
100  }
101 
106  // if the failure occured on request and the message was saved
107  // return that message
108  $articleId = $request->getUserVar('articleId');
109  $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */
110  $article = $submissionDao->getByid($articleId);
111  $failedMsg = $article->getData($this->getFailedMsgSettingName());
112  if (!empty($failedMsg)) {
113  return $failedMsg;
114  }
115  // else check the failure message with Crossref, using the API
116  $context = $request->getContext();
117 
118  import('lib.pkp.classes.helpers.PKPCurlHelper');
119  $curlCh = PKPCurlHelper::getCurlObject();
120 
121  curl_setopt($curlCh, CURLOPT_RETURNTRANSFER, true);
122  curl_setopt($curlCh, CURLOPT_POST, true);
123  curl_setopt($curlCh, CURLOPT_HEADER, 0);
124 
125  // Use a different endpoint for testing and production.
126  $endpoint = ($this->isTestMode($context) ? CROSSREF_API_STAUTS_URL_DEV : CROSSREF_API_STAUTS_URL);
127  curl_setopt($curlCh, CURLOPT_URL, $endpoint);
128  // Set the form post fields
129  $username = $this->getSetting($context->getId(), 'username');
130  $password = $this->getSetting($context->getId(), 'password');
131  $batchId = $request->getUserVar('batchId');
132  $data = array('doi_batch_id' => $batchId, 'type' => 'result', 'usr' => $username, 'pwd' => $password);
133  curl_setopt($curlCh, CURLOPT_POSTFIELDS, $data);
134 
135  $response = curl_exec($curlCh);
136 
137  if ($response === false) {
138  $result = __('plugins.importexport.common.register.error.mdsError', array('param' => 'No response from server.'));
139  } else {
140  $result = $response;
141  }
142  return $result;
143  }
144 
145 
149  function getExportActionNames() {
150  return array(
151  EXPORT_ACTION_DEPOSIT => __('plugins.importexport.crossref.action.register'),
152  EXPORT_ACTION_EXPORT => __('plugins.importexport.crossref.action.export'),
153  EXPORT_ACTION_MARKREGISTERED => __('plugins.importexport.crossref.action.markRegistered'),
154  );
155  }
156 
161  protected function _getObjectAdditionalSettings() {
162  return array_merge(parent::_getObjectAdditionalSettings(), array(
164  $this->getFailedMsgSettingName(),
165  ));
166  }
167 
172  return 'crossref';
173  }
174 
179  return 'CrossRefSettingsForm';
180  }
181 
186  return 'CrossrefExportDeployment';
187  }
188 
192  function executeExportAction($request, $objects, $filter, $tab, $objectsFileNamePart, $noValidation = null) {
193  $context = $request->getContext();
194  $path = array('plugin', $this->getName());
195 
196  import('lib.pkp.classes.file.FileManager');
197  $fileManager = new FileManager();
198  $resultErrors = array();
199 
200  if ($request->getUserVar(EXPORT_ACTION_DEPOSIT)) {
201  assert($filter != null);
202  // Errors occured will be accessible via the status link
203  // thus do not display all errors notifications (for every article),
204  // just one general.
205  // Warnings occured when the registration was successfull will however be
206  // displayed for each article.
207  $errorsOccured = false;
208  // The new Crossref deposit API expects one request per object.
209  // On contrary the export supports bulk/batch object export, thus
210  // also the filter expects an array of objects.
211  // Thus the foreach loop, but every object will be in an one item array for
212  // the export and filter to work.
213  foreach ($objects as $object) {
214  // Get the XML
215  $exportXml = $this->exportXML(array($object), $filter, $context, $noValidation);
216  // Write the XML to a file.
217  // export file name example: crossref-20160723-160036-articles-1-1.xml
218  $objectsFileNamePart = $objectsFileNamePart . '-' . $object->getId();
219  $exportFileName = $this->getExportFileName($this->getExportPath(), $objectsFileNamePart, $context, '.xml');
220  $fileManager->writeFile($exportFileName, $exportXml);
221  // Deposit the XML file.
222  $result = $this->depositXML($object, $context, $exportFileName);
223  if (!$result) {
224  $errorsOccured = true;
225  }
226  if (is_array($result)) {
227  $resultErrors[] = $result;
228  }
229  // Remove all temporary files.
230  $fileManager->deleteByPath($exportFileName);
231  }
232  // send notifications
233  if (empty($resultErrors)) {
234  if ($errorsOccured) {
235  $this->_sendNotification(
236  $request->getUser(),
237  'plugins.importexport.crossref.register.error.mdsError',
238  NOTIFICATION_TYPE_ERROR
239  );
240  } else {
241  $this->_sendNotification(
242  $request->getUser(),
243  $this->getDepositSuccessNotificationMessageKey(),
244  NOTIFICATION_TYPE_SUCCESS
245  );
246  }
247  } else {
248  foreach($resultErrors as $errors) {
249  foreach ($errors as $error) {
250  assert(is_array($error) && count($error) >= 1);
251  $this->_sendNotification(
252  $request->getUser(),
253  $error[0],
254  NOTIFICATION_TYPE_ERROR,
255  (isset($error[1]) ? $error[1] : null)
256  );
257  }
258  }
259  }
260  // redirect back to the right tab
261  $request->redirect(null, null, null, $path, null, $tab);
262  } else {
263  parent::executeExportAction($request, $objects, $filter, $tab, $objectsFileNamePart, $noValidation);
264  }
265  }
266 
274  function depositXML($objects, $context, $filename) {
275  $status = null;
276 
277  import('lib.pkp.classes.helpers.PKPCurlHelper');
278  $curlCh = PKPCurlHelper::getCurlObject();
279 
280  curl_setopt($curlCh, CURLOPT_RETURNTRANSFER, true);
281  curl_setopt($curlCh, CURLOPT_POST, true);
282  curl_setopt($curlCh, CURLOPT_HEADER, 0);
283 
284  // Use a different endpoint for testing and production.
285  $endpoint = ($this->isTestMode($context) ? CROSSREF_API_URL_DEV : CROSSREF_API_URL);
286  curl_setopt($curlCh, CURLOPT_URL, $endpoint);
287  // Set the form post fields
288  $username = $this->getSetting($context->getId(), 'username');
289  $password = $this->getSetting($context->getId(), 'password');
290  assert(is_readable($filename));
291  if (function_exists('curl_file_create')) {
292  curl_setopt($curlCh, CURLOPT_SAFE_UPLOAD, true);
293  $cfile = new CURLFile($filename);
294  } else {
295  $cfile = "@$filename";
296  }
297  $data = array('operation' => 'doMDUpload', 'usr' => $username, 'pwd' => $password, 'mdFile' => $cfile);
298  curl_setopt($curlCh, CURLOPT_POSTFIELDS, $data);
299  $response = curl_exec($curlCh);
300 
301  $msg = null;
302  if ($response === false) {
303  $result = array(array('plugins.importexport.common.register.error.mdsError', 'No response from server.'));
304  } elseif (curl_getinfo($curlCh, CURLINFO_HTTP_CODE) != CROSSREF_API_DEPOSIT_OK) {
305  // These are the failures that occur immediatelly on request
306  // and can not be accessed later, so we save the falure message in the DB
307  $xmlDoc = new DOMDocument();
308  $xmlDoc->loadXML($response);
309  // Get batch ID
310  $batchIdNode = $xmlDoc->getElementsByTagName('batch_id')->item(0);
311  // Get re message
312  $msg = $response;
313  $status = CROSSREF_STATUS_FAILED;
314  $result = false;
315  } else {
316  // Get DOMDocument from the response XML string
317  $xmlDoc = new DOMDocument();
318  $xmlDoc->loadXML($response);
319  $batchIdNode = $xmlDoc->getElementsByTagName('batch_id')->item(0);
320 
321  // Get the DOI deposit status
322  // If the deposit failed
323  $failureCountNode = $xmlDoc->getElementsByTagName('failure_count')->item(0);
324  $failureCount = (int) $failureCountNode->nodeValue;
325  if ($failureCount > 0) {
326  $status = CROSSREF_STATUS_FAILED;
327  $result = false;
328  } else {
329  // Deposit was received
330  $status = EXPORT_STATUS_REGISTERED;
331  $result = true;
332 
333  // If there were some warnings, display them
334  $warningCountNode = $xmlDoc->getElementsByTagName('warning_count')->item(0);
335  $warningCount = (int) $warningCountNode->nodeValue;
336  if ($warningCount > 0) {
337  $result = array(array('plugins.importexport.crossref.register.success.warning', htmlspecialchars($response)));
338  }
339  // A possibility for other plugins (e.g. reference linking) to work with the response
340  HookRegistry::call('crossrefexportplugin::deposited', array($this, $response, $objects));
341  }
342  }
343  // Update the status
344  if ($status) {
345  $this->updateDepositStatus($context, $objects, $status, $batchIdNode->nodeValue, $msg);
346  $this->updateObject($objects);
347  }
348 
349  curl_close($curlCh);
350  return $result;
351  }
352 
361  function updateDepositStatus($context, $object, $status, $batchId, $failedMsg = null) {
362  assert(is_a($object, 'Submission') or is_a($object, 'Issue'));
363  // remove the old failure message, if exists
364  $object->setData($this->getFailedMsgSettingName(), null);
365  $object->setData($this->getDepositStatusSettingName(), $status);
366  $object->setData($this->getDepositBatchIdSettingName(), $batchId);
367  if ($failedMsg) {
368  $object->setData($this->getFailedMsgSettingName(), $failedMsg);
369  }
370  if ($status == EXPORT_STATUS_REGISTERED) {
371  // Save the DOI -- the object will be updated
372  $this->saveRegisteredDoi($context, $object);
373  }
374  }
375 
379  function markRegistered($context, $objects) {
380  foreach ($objects as $object) {
381  // remove the old failure message, if exists
382  $object->setData($this->getFailedMsgSettingName(), null);
383  $object->setData($this->getDepositStatusSettingName(), EXPORT_STATUS_MARKEDREGISTERED);
384  $this->saveRegisteredDoi($context, $object);
385  }
386  }
387 
393  return $this->getPluginSettingsPrefix().'::failedMsg';
394  }
395 
401  return $this->getPluginSettingsPrefix().'::batchId';
402  }
403 
408  return 'plugins.importexport.common.register.success';
409  }
410 
414  function executeCLICommand($scriptName, $command, $context, $outputFile, $objects, $filter, $objectsFileNamePart) {
415  switch ($command) {
416  case 'export':
417  PluginRegistry::loadCategory('generic', true, $context->getId());
418  $exportXml = $this->exportXML($objects, $filter, $context);
419  if ($outputFile) file_put_contents($outputFile, $exportXml);
420  break;
421  case 'register':
422  PluginRegistry::loadCategory('generic', true, $context->getId());
423  import('lib.pkp.classes.file.FileManager');
424  $fileManager = new FileManager();
425  $resultErrors = array();
426  // Errors occured will be accessible via the status link
427  // thus do not display all errors notifications (for every article),
428  // just one general.
429  // Warnings occured when the registration was successfull will however be
430  // displayed for each article.
431  $errorsOccured = false;
432  // The new Crossref deposit API expects one request per object.
433  // On contrary the export supports bulk/batch object export, thus
434  // also the filter expects an array of objects.
435  // Thus the foreach loop, but every object will be in an one item array for
436  // the export and filter to work.
437  foreach ($objects as $object) {
438  // Get the XML
439  $exportXml = $this->exportXML(array($object), $filter, $context);
440  // Write the XML to a file.
441  // export file name example: crossref-20160723-160036-articles-1-1.xml
442  $objectsFileNamePartId = $objectsFileNamePart . '-' . $object->getId();
443  $exportFileName = $this->getExportFileName($this->getExportPath(), $objectsFileNamePartId, $context, '.xml');
444  $fileManager->writeFile($exportFileName, $exportXml);
445  // Deposit the XML file.
446  $result = $this->depositXML($object, $context, $exportFileName);
447  if (!$result) {
448  $errorsOccured = true;
449  }
450  if (is_array($result)) {
451  $resultErrors[] = $result;
452  }
453  // Remove all temporary files.
454  $fileManager->deleteByPath($exportFileName);
455  }
456  // display deposit result status messages
457  if (empty($resultErrors)) {
458  if ($errorsOccured) {
459  echo __('plugins.importexport.crossref.register.error.mdsError') . "\n";
460  } else {
461  echo __('plugins.importexport.common.register.success') . "\n";
462  }
463  } else {
464  echo __('plugins.importexport.common.cliError') . "\n";
465  foreach($resultErrors as $errors) {
466  foreach ($errors as $error) {
467  assert(is_array($error) && count($error) >= 1);
468  $errorMessage = __($error[0], array('param' => (isset($error[1]) ? $error[1] : null)));
469  echo "*** $errorMessage\n";
470  }
471  }
472  echo "\n";
473  $this->usage($scriptName);
474  }
475  break;
476  }
477  }
478 }
479 
480 
CrossRefExportPlugin\getPluginSettingsPrefix
getPluginSettingsPrefix()
Definition: CrossRefExportPlugin.inc.php:171
CrossRefExportPlugin\getDepositSuccessNotificationMessageKey
getDepositSuccessNotificationMessageKey()
Definition: CrossRefExportPlugin.inc.php:407
PubObjectsExportPlugin\getDepositStatusSettingName
getDepositStatusSettingName()
Definition: PubObjectsExportPlugin.inc.php:521
PubObjectsExportPlugin\_sendNotification
_sendNotification($user, $message, $notificationType, $param=null)
Definition: PubObjectsExportPlugin.inc.php:709
CrossRefExportPlugin\updateDepositStatus
updateDepositStatus($context, $object, $status, $batchId, $failedMsg=null)
Definition: CrossRefExportPlugin.inc.php:361
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
DOIPubIdExportPlugin
Basis class for DOI XML metadata export plugins.
Definition: DOIPubIdExportPlugin.inc.php:24
CrossRefExportPlugin\getName
getName()
Definition: CrossRefExportPlugin.inc.php:41
CrossRefExportPlugin\getDisplayName
getDisplayName()
Definition: CrossRefExportPlugin.inc.php:48
CrossRefExportPlugin\getStatusNames
getStatusNames()
Definition: CrossRefExportPlugin.inc.php:69
CrossRefExportPlugin\getFailedMsgSettingName
getFailedMsgSettingName()
Definition: CrossRefExportPlugin.inc.php:392
CrossRefExportPlugin\getDescription
getDescription()
Definition: CrossRefExportPlugin.inc.php:55
PKPCurlHelper\getCurlObject
static getCurlObject($url=null, $useProxySettings=true)
Definition: PKPCurlHelper.inc.php:23
PubObjectsExportPlugin\isTestMode
isTestMode($context)
Definition: PubObjectsExportPlugin.inc.php:513
PubObjectsExportPlugin\exportXML
exportXML($objects, $filter, $context, $noValidation=null)
Definition: PubObjectsExportPlugin.inc.php:385
PluginRegistry\loadCategory
static loadCategory($category, $enabledOnly=false, $mainContextId=null)
Definition: PluginRegistry.inc.php:103
DOIPubIdExportPlugin\saveRegisteredDoi
saveRegisteredDoi($context, $object, $testPrefix='10.1234')
Definition: DOIPubIdExportPlugin.inc.php:96
CrossRefExportPlugin\getSettingsFormClassName
getSettingsFormClassName()
Definition: CrossRefExportPlugin.inc.php:178
ImportExportPlugin\getExportFileName
getExportFileName($basePath, $objectsFileNamePart, $context, $extension='.xml')
Definition: ImportExportPlugin.inc.php:152
Plugin\getSetting
getSetting($contextId, $name)
Definition: Plugin.inc.php:473
CrossRefExportPlugin\getStatusActions
getStatusActions($pubObject)
Definition: CrossRefExportPlugin.inc.php:80
CrossRefExportPlugin\getExportActionNames
getExportActionNames()
Definition: CrossRefExportPlugin.inc.php:149
ImportExportPlugin\getExportPath
getExportPath()
Definition: ImportExportPlugin.inc.php:140
CrossRefExportPlugin\executeExportAction
executeExportAction($request, $objects, $filter, $tab, $objectsFileNamePart, $noValidation=null)
Definition: CrossRefExportPlugin.inc.php:192
AjaxModal
A modal that retrieves its content from via AJAX.
Definition: AjaxModal.inc.php:18
LinkAction
Base class defining an action that can be performed by the user in the user interface.
Definition: LinkAction.inc.php:22
CrossRefExportPlugin\executeCLICommand
executeCLICommand($scriptName, $command, $context, $outputFile, $objects, $filter, $objectsFileNamePart)
Definition: CrossRefExportPlugin.inc.php:414
PubObjectsExportPlugin\usage
usage($scriptName)
Definition: PubObjectsExportPlugin.inc.php:530
CrossRefExportPlugin\getDepositBatchIdSettingName
getDepositBatchIdSettingName()
Definition: CrossRefExportPlugin.inc.php:400
CrossRefExportPlugin
CrossRef/MEDLINE XML metadata export plugin.
Definition: CrossRefExportPlugin.inc.php:36
CrossRefExportPlugin\getSubmissionFilter
getSubmissionFilter()
Definition: CrossRefExportPlugin.inc.php:62
PubObjectsExportPlugin\updateObject
updateObject($object)
Definition: PubObjectsExportPlugin.inc.php:421
Plugin\$request
$request
Definition: Plugin.inc.php:68
CrossRefExportPlugin\_getObjectAdditionalSettings
_getObjectAdditionalSettings()
Definition: CrossRefExportPlugin.inc.php:161
CrossRefExportPlugin\markRegistered
markRegistered($context, $objects)
Definition: CrossRefExportPlugin.inc.php:379
PKPApplication\get
static get()
Definition: PKPApplication.inc.php:235
FileManager
Class defining basic operations for file management.
Definition: FileManager.inc.php:35
HookRegistry\call
static call($hookName, $args=null)
Definition: HookRegistry.inc.php:86
CrossRefExportPlugin\depositXML
depositXML($objects, $context, $filename)
Definition: CrossRefExportPlugin.inc.php:274
CrossRefExportPlugin\getExportDeploymentClassName
getExportDeploymentClassName()
Definition: CrossRefExportPlugin.inc.php:185
CrossRefExportPlugin\getStatusMessage
getStatusMessage($request)
Definition: CrossRefExportPlugin.inc.php:105