Open Journal Systems  3.3.0
ArticleReportPlugin.inc.php
1 <?php
2 
16 import('lib.pkp.classes.plugins.ReportPlugin');
17 
22  function register($category, $path, $mainContextId = null) {
23  $success = parent::register($category, $path, $mainContextId);
24  $this->addLocaleData();
25  return $success;
26  }
27 
33  function getName() {
34  return 'ArticleReportPlugin';
35  }
36 
40  function getDisplayName() {
41  return __('plugins.reports.articles.displayName');
42  }
43 
47  function getDescription() {
48  return __('plugins.reports.articles.description');
49  }
50 
54  function display($args, $request) {
55  $journal = $request->getJournal();
56  $acronym = PKPString::regexp_replace("/[^A-Za-z0-9 ]/", '', $journal->getLocalizedAcronym());
57 
58  // Prepare for UTF8-encoded CSV output.
59  header('content-type: text/comma-separated-values');
60  header('content-disposition: attachment; filename=articles-' . $acronym . '-' . date('Ymd') . '.csv');
61  $fp = fopen('php://output', 'wt');
62  // Add BOM (byte order mark) to fix UTF-8 in Excel
63  fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
64 
65  $submissionDao = DAORegistry::getDAO('SubmissionDAO'); /* @var $submissionDao SubmissionDAO */
66  $editDecisionDao = DAORegistry::getDAO('EditDecisionDAO'); /* @var $editDecisionDao EditDecisionDAO */
67  $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */
68  $userGroupDao = DAORegistry::getDAO('UserGroupDAO'); /* @var $userGroupDao UserGroupDAO */
69  $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */
70  $sectionDao = DAORegistry::getDAO('SectionDAO'); /* @var $sectionDao SectionDAO */
71  $submissionKeywordDao = DAORegistry::getDAO('SubmissionKeywordDAO'); /* @var $submissionKeywordDao SubmissionKeywordDAO */
72  $submissionSubjectDao = DAORegistry::getDAO('SubmissionSubjectDAO'); /* @var $submissionSubjectDao SubmissionSubjectDAO */
73  $submissionDisciplineDao = DAORegistry::getDAO('SubmissionDisciplineDAO'); /* @var $submissionDisciplineDao SubmissionDisciplineDAO */
74  $submissionAgencyDao = DAORegistry::getDAO('SubmissionAgencyDAO'); /* @var $submissionAgencyDao SubmissionAgencyDAO */
75 
76  $editorUserGroupIds = array_map(function($userGroup) {
77  return $userGroup->getId();
78  }, array_filter($userGroupDao->getByContextId($journal->getId())->toArray(), function($userGroup) {
79  return in_array($userGroup->getRoleId(), [ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR]);
80  }));
81 
82  AppLocale::requireComponents(LOCALE_COMPONENT_APP_EDITOR, LOCALE_COMPONENT_PKP_SUBMISSION, LOCALE_COMPONENT_PKP_READER);
83 
84  // Load the data from the database and store it in an array.
85  // (This must be stored before display because we won't know the data
86  // dimensions until it has all been loaded.)
87  $results = $sectionTitles = [];
88  $submissions = $submissionDao->getByContextId($journal->getId());
89  $maxAuthors = $maxEditors = $maxDecisions = 0;
90  while ($submission = $submissions->next()) {
91  $publication = $submission->getCurrentPublication();
92  $maxAuthors = max($maxAuthors, count($publication->getData('authors')));
93  $editDecisions = $editDecisionDao->getEditorDecisions($submission->getId());
94  $statusMap = $submission->getStatusMap();
95 
96  // Count the highest number of decisions per editor.
97  $editDecisionsPerEditor = [];
98  foreach ($editDecisions as $editDecision) {
99  $editorId = $editDecision['editorId'];
100  $editDecisionsPerEditor[$editorId] = ($editDecisionsPerEditor[$editorId] ?? 0) + 1;
101  $maxDecisions = max($maxDecisions, $editDecisionsPerEditor[$editorId]);
102  }
103 
104  // Load editor and decision information
105  $stageAssignmentsFactory = $stageAssignmentDao->getBySubmissionAndStageId($submission->getId());
106  $editors = $editorsById = [];
107  while ($stageAssignment = $stageAssignmentsFactory->next()) {
108  $userId = $stageAssignment->getUserId();
109  if (!in_array($stageAssignment->getUserGroupId(), $editorUserGroupIds)) continue;
110  if (isset($editors[$userId])) continue;
111  if (!isset($editorsById[$userId])) {
112  $editor = $userDao->getById($userId);
113  $editorsById[$userId] = [
114  $editor->getLocalizedGivenName(),
115  $editor->getLocalizedFamilyName(),
116  $editor->getData('orcid'),
117  $editor->getEmail(),
118  ];
119  }
120  $editors[$userId] = $editorsById[$userId];
121  $maxEditors = max($maxEditors, count($editors));
122  }
123 
124  // Load section title information
125  $sectionId = $publication->getData('sectionId');
126  if (!isset($sectionTitles[$sectionId])) {
127  $section = $sectionDao->getById($sectionId);
128  $sectionTitles[$sectionId] = $section->getLocalizedTitle();
129  }
130 
131  // Store the submission results
132  $results[] = [
133  'submissionId' => $submission->getId(),
134  'title' => $publication->getLocalizedFullTitle(),
135  'abstract' => html_entity_decode(strip_tags($publication->getLocalizedData('abstract'))),
136  'authors' => array_map(function($author) {
137  return [
138  $author->getLocalizedGivenName(),
139  $author->getLocalizedFamilyName(),
140  $author->getData('orcid'),
141  $author->getData('country'),
142  $author->getLocalizedData('affiliation'),
143  $author->getData('email'),
144  $author->getData('url'),
145  html_entity_decode(strip_tags($author->getLocalizedData('biography'))),
146  ];
147  }, $publication->getData('authors')),
148  'sectionTitle' => $sectionTitles[$sectionId],
149  'language' => $publication->getData('locale'),
150  'coverage' => $publication->getLocalizedData('coverage'),
151  'rights' => $publication->getLocalizedData('rights'),
152  'source' => $publication->getLocalizedData('source'),
153  'subjects' => join(', ', $submissionSubjectDao->getSubjects($submission->getCurrentPublication()->getId(), array($submission->getLocale()))[$submission->getLocale()]??[]),
154  'type' => $publication->getLocalizedData('type'),
155  'disciplines' => join(', ', $submissionDisciplineDao->getDisciplines($submission->getCurrentPublication()->getId(), array($submission->getLocale()))[$submission->getLocale()]??[]),
156  'keywords' => join(', ', $submissionKeywordDao->getKeywords($submission->getCurrentPublication()->getId(), array($submission->getLocale()))[$submission->getLocale()]??[]),
157  'agencies' => join(', ', $submissionAgencyDao->getAgencies($submission->getCurrentPublication()->getId(), array($submission->getLocale()))[$submission->getLocale()]??[]),
158  'status' => $submission->getStatus() == STATUS_QUEUED ? $this->getStageLabel($submission->getStageId()) : __($statusMap[$submission->getStatus()]),
159  'url' => $request->url(null, 'workflow', 'access', $submission->getId()),
160  'doi' => $submission->getStoredPubId('doi'),
161  'dateSubmitted' => $submission->getDateSubmitted(),
162  'lastModified' => $submission->getLastModified(),
163  'editors' => $editors,
164  'decisions' => $editDecisions,
165  ];
166  }
167 
168  // Build and display the column headers.
169  $columns = [
170  __('article.submissionId'),
171  __('article.title'),
172  __('article.abstract')
173  ];
174 
175  $authorColumnCount = $editorColumnCount = $decisionColumnCount = 0;
176  for ($a=1; $a<=$maxAuthors; $a++) {
177  $columns = array_merge($columns, $authorColumns = [
178  __('user.givenName') . " (" . __('user.role.author') . " $a)",
179  __('user.familyName') . " (" . __('user.role.author') . " $a)",
180  __('user.orcid') . " (" . __('user.role.author') . " $a)",
181  __('common.country') . " (" . __('user.role.author') . " $a)",
182  __('user.affiliation') . " (" . __('user.role.author') . " $a)",
183  __('user.email') . " (" . __('user.role.author') . " $a)",
184  __('user.url') . " (" . __('user.role.author') . " $a)",
185  __('user.biography') . " (" . __('user.role.author') . " $a)"
186  ]);
187  $authorColumnCount = count($authorColumns);
188  }
189 
190  $columns = array_merge($columns, [
191  __('section.title'),
192  __('common.language'),
193  __('article.coverage'),
194  __('submission.rights'),
195  __('submission.source'),
196  __('common.subjects'),
197  __('common.type'),
198  __('search.discipline'),
199  __('common.keywords'),
200  __('submission.supportingAgencies'),
201  __('common.status'),
202  __('common.url'),
203  __('metadata.property.displayName.doi'),
204  __('common.dateSubmitted'),
205  __('submission.lastModified'),
206  ]);
207 
208  for ($e = 1; $e <= $maxEditors; $e++) {
209  $columns = array_merge($columns, $editorColumns = [
210  __('user.givenName') . " (" . __('user.role.editor') . " $e)",
211  __('user.familyName') . " (" . __('user.role.editor') . " $e)",
212  __('user.orcid') . " (" . __('user.role.editor') . " $e)",
213  __('user.email') . " (" . __('user.role.editor') . " $e)",
214  ]);
215  $editorColumnCount = count($editorColumns);
216  for ($d = 1; $d <= $maxDecisions; $d++) {
217  $columns = array_merge($columns, $decisionColumns = [
218  __('submission.editorDecision') . " $d " . " (" . __('user.role.editor') . " $e)",
219  __('common.dateDecided') . " $d " . " (" . __('user.role.editor') . " $e)"
220  ]);
221  $decisionColumnCount = count($decisionColumns);
222  }
223  }
224  fputcsv($fp, array_values($columns));
225 
226  // Display the data rows.
227  foreach ($results as $result) {
228  $row = [];
229  foreach ($result as $column => $value) switch ($column) {
230  case 'authors':
231  for ($i=0; $i<$maxAuthors; $i++) {
232  $row = array_merge($row, $value[$i] ?? array_fill(0, $authorColumnCount, ''));
233  }
234  break;
235  case 'editors':
236  $editorIds = array_keys($value);
237  $editorEntries = array_values($value);
238  for ($i=0; $i<$maxEditors; $i++) {
239  $submissionHasThisEditor = isset($editorEntries[$i]);
240  $row = array_merge($row, $submissionHasThisEditor ? $editorEntries[$i] : array_fill(0, $editorColumnCount, ''));
241  for ($j=0; $j<$maxDecisions; $j++) {
242  if (!$submissionHasThisEditor) {
243  $row = array_merge($row, array_fill(0, $decisionColumnCount, ''));
244  continue;
245  }
246 
247  $editorId = $editorIds[$i];
248  $latestDecision = $latestDecisionDate = '';
249  $decisionCounter = 0;
250  foreach ($result['decisions'] as $decision) {
251  if ($decision['editorId'] != $editorId) continue;
252  if ($j != $decisionCounter++) continue;
253  $latestDecision = $this->getDecisionMessage($decision['decision']);
254  $latestDecisionDate = $decision['dateDecided'];
255  }
256  $row = array_merge($row, [$latestDecision, $latestDecisionDate]);
257  }
258  }
259  break;
260  case 'decisions':
261  break; // Handled in the 'editors' case
262  default: $row[] = $value; // Other columns can be sent as they are.
263  }
264  fputcsv($fp, $row);
265  }
266 
267  fclose($fp);
268  }
269 
275  function getStageLabel($stageId) {
276  switch ($stageId) {
277  case WORKFLOW_STAGE_ID_SUBMISSION:
278  return __('submission.submission');
279  case WORKFLOW_STAGE_ID_EXTERNAL_REVIEW:
280  return __('submission.review');
281  case WORKFLOW_STAGE_ID_EDITING:
282  return __('submission.copyediting');
283  case WORKFLOW_STAGE_ID_PRODUCTION:
284  return __('submission.production');
285  }
286  return '';
287  }
288 
294  function getDecisionMessage($decision) {
295  import('classes.workflow.EditorDecisionActionsManager'); // SUBMISSION_EDITOR_...
296  switch ($decision) {
297  case SUBMISSION_EDITOR_DECISION_ACCEPT:
298  return __('editor.submission.decision.accept');
299  case SUBMISSION_EDITOR_DECISION_PENDING_REVISIONS:
300  return __('editor.submission.decision.requestRevisions');
301  case SUBMISSION_EDITOR_DECISION_RESUBMIT:
302  return __('editor.submission.decision.resubmit');
303  case SUBMISSION_EDITOR_DECISION_DECLINE:
304  return __('editor.submission.decision.decline');
305  case SUBMISSION_EDITOR_DECISION_SEND_TO_PRODUCTION:
306  return __('editor.submission.decision.sendToProduction');
307  case SUBMISSION_EDITOR_DECISION_EXTERNAL_REVIEW:
308  return __('editor.submission.decision.sendExternalReview');
309  case SUBMISSION_EDITOR_DECISION_INITIAL_DECLINE:
310  return __('editor.submission.decision.decline');
311  case SUBMISSION_EDITOR_RECOMMEND_ACCEPT:
312  return __('editor.submission.recommendation.display', array('recommendation' => __('editor.submission.decision.accept')));
313  case SUBMISSION_EDITOR_RECOMMEND_DECLINE:
314  return __('editor.submission.recommendation.display', array('recommendation' => __('editor.submission.decision.decline')));
315  case SUBMISSION_EDITOR_RECOMMEND_PENDING_REVISIONS:
316  return __('editor.submission.recommendation.display', array('recommendation' => __('editor.submission.decision.requestRevisions')));
317  case SUBMISSION_EDITOR_RECOMMEND_RESUBMIT:
318  return __('editor.submission.recommendation.display', array('recommendation' => __('editor.submission.decision.resubmit')));
319  default:
320  return '';
321  }
322  }
323 }
324 
AppLocale\requireComponents
static requireComponents()
Definition: env1/MockAppLocale.inc.php:56
PKPString\regexp_replace
static regexp_replace($pattern, $replacement, $subject, $limit=-1)
Definition: PKPString.inc.php:279
ArticleReportPlugin\getDisplayName
getDisplayName()
Definition: ArticleReportPlugin.inc.php:40
ArticleReportPlugin\getDescription
getDescription()
Definition: ArticleReportPlugin.inc.php:47
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
ArticleReportPlugin
Article report plugin.
Definition: ArticleReportPlugin.inc.php:18
ArticleReportPlugin\getDecisionMessage
getDecisionMessage($decision)
Definition: ArticleReportPlugin.inc.php:294
ReportPlugin
Abstract class for report plugins.
Definition: ReportPlugin.inc.php:18
Plugin\$request
$request
Definition: Plugin.inc.php:68
Plugin\addLocaleData
addLocaleData($locale=null)
Definition: Plugin.inc.php:454
ArticleReportPlugin\getStageLabel
getStageLabel($stageId)
Definition: ArticleReportPlugin.inc.php:275
ArticleReportPlugin\display
display($args, $request)
Definition: ArticleReportPlugin.inc.php:54
ArticleReportPlugin\getName
getName()
Definition: ArticleReportPlugin.inc.php:33