Open Journal Systems  3.3.0
PKPFileUploadWizardHandler.inc.php
1 <?php
2 
17 // Import the base handler.
18 import('classes.handler.Handler');
19 
20 // Import JSON class for use with all AJAX requests.
21 import('lib.pkp.classes.core.JSONMessage');
22 
25  var $_fileStage;
26 
29 
31  var $_revisionOnly;
32 
35 
37  var $_revisedFileId;
38 
41 
43  var $_assocId;
44 
45 
49  function __construct() {
50  parent::__construct();
51  $this->addRoleAssignment(
52  array(ROLE_ID_MANAGER, ROLE_ID_SUB_EDITOR, ROLE_ID_AUTHOR, ROLE_ID_REVIEWER, ROLE_ID_ASSISTANT),
53  array(
54  'startWizard', 'displayFileUploadForm',
55  'uploadFile', 'confirmRevision',
56  'editMetadata',
57  'finishFileSubmission'
58  )
59  );
60  }
61 
62 
63  //
64  // Implement template methods from PKPHandler
65  //
69  function initialize($request) {
70  parent::initialize($request);
71  // Configure the wizard with the authorized submission and file stage.
72  // Validated in authorize.
73  $this->_fileStage = (int)$request->getUserVar('fileStage');
74 
75  // Set the uploader roles (if given).
76  $uploaderRoles = $request->getUserVar('uploaderRoles');
77  if (!empty($uploaderRoles)) {
78  $this->_uploaderRoles = array();
79  $uploaderRoles = explode('-', $uploaderRoles);
80  foreach($uploaderRoles as $uploaderRole) {
81  if (!is_numeric($uploaderRole)) fatalError('Invalid uploader role!');
82  $this->_uploaderRoles[] = (int)$uploaderRole;
83  }
84  }
85 
86  // Do we allow revisions only?
87  $this->_revisionOnly = (boolean)$request->getUserVar('revisionOnly');
88  $reviewRound = $this->getReviewRound();
89  $this->_assocType = $request->getUserVar('assocType') ? (int)$request->getUserVar('assocType') : null;
90  $this->_assocId = $request->getUserVar('assocId') ? (int)$request->getUserVar('assocId') : null;
91 
92  // The revised file will be non-null if we revise a single existing file.
93  if ($this->getRevisionOnly() && $request->getUserVar('revisedFileId')) {
94  // Validated in authorize.
95  $this->_revisedFileId = (int)$request->getUserVar('revisedFileId');
96  }
97 
98  // Load translations.
100  LOCALE_COMPONENT_APP_SUBMISSION,
101  LOCALE_COMPONENT_PKP_SUBMISSION,
102  LOCALE_COMPONENT_PKP_COMMON,
103  LOCALE_COMPONENT_APP_COMMON
104  );
105  }
106 
107 
108  //
109  // Getters and Setters
110  //
115  function getSubmission() {
116  return $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION);
117  }
118 
119 
124  function getStageId() {
125  return $this->getAuthorizedContextObject(ASSOC_TYPE_WORKFLOW_STAGE);
126  }
127 
134  function getFileStage() {
135  return $this->_fileStage;
136  }
137 
142  function getUploaderRoles() {
143  return $this->_uploaderRoles;
144  }
145 
150  function getRevisionOnly() {
151  return $this->_revisionOnly;
152  }
153 
158  function getReviewRound() {
159  return $this->getAuthorizedContextObject(ASSOC_TYPE_REVIEW_ROUND);
160  }
161 
166  function getRevisedFileId() {
167  return $this->_revisedFileId;
168  }
169 
174  function getAssocType() {
175  return $this->_assocType;
176  }
177 
182  function getAssocId() {
183  return $this->_assocId;
184  }
185 
186  //
187  // Public handler methods
188  //
195  function startWizard($args, $request) {
196  $templateMgr = TemplateManager::getManager($request);
197  $reviewRound = $this->getReviewRound();
198  $templateMgr->assign(array(
199  'submissionId' => $this->getSubmission()->getId(),
200  'stageId' => $this->getStageId(),
201  'uploaderRoles' => implode('-', (array) $this->getUploaderRoles()),
202  'fileStage' => $this->getFileStage(),
203  'isReviewer' => $request->getUserVar('isReviewer'),
204  'revisionOnly' => $this->getRevisionOnly(),
205  'reviewRoundId' => is_a($reviewRound, 'ReviewRound')?$reviewRound->getId():null,
206  'revisedFileId' => $this->getRevisedFileId(),
207  'assocType' => $this->getAssocType(),
208  'assocId' => $this->getAssocId(),
209  'dependentFilesOnly' => $request->getUserVar('dependentFilesOnly'),
210  ));
211  return $templateMgr->fetchJson('controllers/wizard/fileUpload/fileUploadWizard.tpl');
212  }
213 
220  function displayFileUploadForm($args, $request) {
221  // Instantiate, configure and initialize the form.
222  import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadForm');
223  $submission = $this->getSubmission();
224  $fileForm = new SubmissionFilesUploadForm(
225  $request, $submission->getId(), $this->getStageId(), $this->getUploaderRoles(), $this->getFileStage(),
226  $this->getRevisionOnly(), $this->getReviewRound(), $this->getRevisedFileId(),
227  $this->getAssocType(), $this->getAssocId()
228  );
229  $fileForm->initData();
230 
231  // Render the form.
232  return new JSONMessage(true, $fileForm->fetch($request));
233  }
234 
241  function uploadFile($args, $request) {
242  // Instantiate the file upload form.
243  $submission = $this->getSubmission();
244  import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadForm');
245  $uploadForm = new SubmissionFilesUploadForm(
246  $request, $submission->getId(), $this->getStageId(), null, $this->getFileStage(),
247  $this->getRevisionOnly(), $this->getReviewRound(), null, $this->getAssocType(), $this->getAssocId()
248  );
249  $uploadForm->readInputData();
250 
251  // Validate the form and upload the file.
252  if (!$uploadForm->validate()) {
253  return new JSONMessage(true, $uploadForm->fetch($request));
254  }
255 
256  $uploadedFile = $uploadForm->execute(); /* @var $uploadedFile SubmissionFile */
257  if (!is_a($uploadedFile, 'SubmissionFile')) {
258  return new JSONMessage(false, __('common.uploadFailed'));
259  }
260 
261  $this->_attachEntities($uploadedFile);
262 
263  // Retrieve file info to be used in a JSON response.
264  $uploadedFileInfo = $this->_getUploadedFileInfo($uploadedFile);
265  $reviewRound = $this->getReviewRound();
266 
267  // If no revised file id was given then try out whether
268  // the user maybe accidentally didn't identify this file as a revision.
269  if (!$uploadForm->getRevisedFileId()) {
270  $user = $request->getUser();
271  $revisionSubmissionFilesSelection = $uploadForm->getRevisionSubmissionFilesSelection($user, $uploadedFile);
272  $revisedFileId = $this->_checkForRevision($uploadedFile, $revisionSubmissionFilesSelection);
273  if ($revisedFileId) {
274  // Instantiate the revision confirmation form.
275  import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadConfirmationForm');
276  $confirmationForm = new SubmissionFilesUploadConfirmationForm($request, $submission->getId(), $this->getStageId(), $this->getFileStage(), $reviewRound, $revisedFileId, $this->getAssocType(), $this->getAssocId(), $uploadedFile);
277  $confirmationForm->initData();
278 
279  // Render the revision confirmation form.
280  return new JSONMessage(true, $confirmationForm->fetch($request), '0', $uploadedFileInfo);
281  }
282  }
283 
284  // Advance to the next step (i.e. meta-data editing).
285  return new JSONMessage(true, '', '0', $uploadedFileInfo);
286  }
287 
292  protected function _attachEntities($submissionFile) {
293  switch ($submissionFile->getFileStage()) {
294  case SUBMISSION_FILE_ATTACHMENT:
295  // If this attachment was created in the review stage, add it to
296  // the review round.
297  if ($reviewRound = $this->getReviewRound()) {
298  $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */
299  $submissionFileDao->assignRevisionToReviewRound($submissionFile->getFileId(), $submissionFile->getRevision(), $reviewRound);
300  }
301  break;
302  case SUBMISSION_FILE_REVIEW_FILE:
303  case SUBMISSION_FILE_REVIEW_ATTACHMENT:
304  case SUBMISSION_FILE_REVIEW_REVISION:
305  // Add the uploaded review file to the review round.
306  $reviewRound = $this->getReviewRound();
307  $submissionFileDao = DAORegistry::getDAO('SubmissionFileDAO'); /* @var $submissionFileDao SubmissionFileDAO */
308  $submissionFileDao->assignRevisionToReviewRound($submissionFile->getFileId(), $submissionFile->getRevision(), $reviewRound);
309 
310  if ($submissionFile->getFileStage() == SUBMISSION_FILE_REVIEW_REVISION) {
311  // Get a list of author user IDs
312  $authorUserIds = array();
313  $stageAssignmentDao = DAORegistry::getDAO('StageAssignmentDAO'); /* @var $stageAssignmentDao StageAssignmentDAO */
314  $submitterAssignments = $stageAssignmentDao->getBySubmissionAndRoleId($reviewRound->getSubmissionId(), ROLE_ID_AUTHOR);
315  while ($assignment = $submitterAssignments->next()) {
316  $authorUserIds[] = $assignment->getUserId();
317  }
318 
319  // Update the task notifications
320  $notificationMgr = new NotificationManager();
321  $notificationMgr->updateNotification(
322  Application::get()->getRequest(),
323  array(NOTIFICATION_TYPE_PENDING_INTERNAL_REVISIONS, NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS),
324  $authorUserIds,
325  ASSOC_TYPE_SUBMISSION,
326  $reviewRound->getSubmissionId()
327  );
328 
329  // Update the ReviewRound status when revision is submitted
330  import('lib.pkp.classes.submission.reviewRound.ReviewRoundDAO');
331  $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); /* @var $reviewRoundDao ReviewRoundDAO */
332  $reviewRoundDao->updateStatus($reviewRound);
333 
334  // Notify editors about the revision upload
335  $submission = $this->getSubmission();
336  $request = Application::get()->getRequest();
337  $router = $request->getRouter();
338  $dispatcher = $router->getDispatcher();
339  $context = $request->getContext();
340  $uploader = $request->getUser();
341  // If the file is uploaded by an author
342  if (in_array($uploader->getId(), $authorUserIds)) {
343 
344  // Fetch the latest notification email timestamp if any
345  import('lib.pkp.classes.log.SubmissionEmailLogEntry'); // Import email event constants
346  $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /* @var $submissionEmailLogDao SubmissionEmailLogDAO */
347  $submissionEmails = $submissionEmailLogDao->getByEventType($submission->getId(), SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION);
348  $lastNotification = null;
349  $sentDates = array();
350  if ($submissionEmails){
351  while ($email = $submissionEmails->next()) {
352  if ($email->getDateSent()){
353  $sentDates[] = $email->getDateSent();
354  }
355  }
356  if (!empty($sentDates)){
357  $lastNotification = max(array_map('strtotime', $sentDates));
358  }
359  }
360 
361  import('lib.pkp.classes.mail.SubmissionMailTemplate');
362  $mail = new SubmissionMailTemplate($submission, 'REVISED_VERSION_NOTIFY');
363  $mail->setEventType(SUBMISSION_EMAIL_AUTHOR_NOTIFY_REVISED_VERSION);
364  $mail->setReplyTo($context->getData('contactEmail'), $context->getData('contactName'));
365  // Get editors assigned to the submission, consider also the recommendOnly editors
366  $userDao = DAORegistry::getDAO('UserDAO'); /* @var $userDao UserDAO */
367  $editorsStageAssignments = $stageAssignmentDao->getEditorsAssignedToStage($submission->getId(), $this->getStageId());
368  foreach ($editorsStageAssignments as $editorsStageAssignment) {
369  $editor = $userDao->getById($editorsStageAssignment->getUserId());
370  // If no prior notification exists OR if editor has logged in after the last revision upload OR the last upload and notification was sent more than a day ago, send a new notification
371  if (is_null($lastNotification) || strtotime($editor->getDateLastLogin()) > $lastNotification || strtotime('-1 day') > $lastNotification){
372  $mail->addRecipient($editor->getEmail(), $editor->getFullName());
373  }
374  }
375  // Get uploader name
376  $submissionUrl = $dispatcher->url($request, ROUTE_PAGE, null, 'workflow', 'index', array($submission->getId(), $this->getStageId()));
377  $mail->assignParams(array(
378  'authorName' => $uploader->getFullName(),
379  'editorialContactSignature' => $context->getData('contactName'),
380  'submissionUrl' => $submissionUrl,
381  ));
382 
383  if ($mail->getRecipients()){
384  if (!$mail->send($request)) {
385  import('classes.notification.NotificationManager');
386  $notificationMgr = new NotificationManager();
387  $notificationMgr->createTrivialNotification($request->getUser()->getId(), NOTIFICATION_TYPE_ERROR, array('contents' => __('email.compose.error')));
388  }
389  }
390 
391  }
392  }
393  break;
394  }
395  }
396 
404  function confirmRevision($args, $request) {
405  // Instantiate the revision confirmation form.
406  $submission = $this->getSubmission();
407  import('lib.pkp.controllers.wizard.fileUpload.form.SubmissionFilesUploadConfirmationForm');
408  // FIXME?: need assocType and assocId? Not sure if they would be used, so not adding now.
409  $reviewRound = $this->getReviewRound();
410  $confirmationForm = new SubmissionFilesUploadConfirmationForm(
411  $request, $submission->getId(), $this->getStageId(), $this->getFileStage(), $reviewRound
412  );
413  $confirmationForm->readInputData();
414 
415  // Validate the form and revise the file.
416  if ($confirmationForm->validate()) {
417  if (is_a($uploadedFile = $confirmationForm->execute(), 'SubmissionFile')) {
418 
419  $this->_attachEntities($uploadedFile);
420 
421  // Go to the meta-data editing step.
422  return new JSONMessage(true, '', '0', $this->_getUploadedFileInfo($uploadedFile));
423  } else {
424 
425  return new JSONMessage(false, __('common.uploadFailed'));
426  }
427  } else {
428  return new JSONMessage(true, $confirmationForm->fetch($request));
429  }
430  }
431 
439  function editMetadata($args, $request) {
440  $metadataForm = $this->_getMetadataForm($request);
441  $metadataForm->initData();
442  return new JSONMessage(true, $metadataForm->fetch($request));
443  }
444 
451  function finishFileSubmission($args, $request) {
452  $submission = $this->getSubmission();
453 
454  // Validation not req'd -- just generating a JSON update message.
455  $fileId = (int)$request->getUserVar('fileId');
456 
457  $templateMgr = TemplateManager::getManager($request);
458  $templateMgr->assign('submissionId', $submission->getId());
459  $templateMgr->assign('fileId', $fileId);
460  if (isset($args['fileStage'])) {
461  $templateMgr->assign('fileStage', $args['fileStage']);
462  }
463 
464  return $templateMgr->fetchJson('controllers/wizard/fileUpload/form/fileSubmissionComplete.tpl');
465  }
466 
467 
468  //
469  // Private helper methods
470  //
476  function _getMetadataForm($request) {
477  $submissionFile = $this->getAuthorizedContextObject(ASSOC_TYPE_SUBMISSION_FILE);
478  return $submissionFile->getMetadataForm($this->getStageId(), $this->getReviewRound());
479  }
480 
490  function &_checkForRevision(&$uploadedFile, &$submissionFiles) {
491  // Get the file name.
492  $uploadedFileName = $uploadedFile->getOriginalFileName();
493 
494  // Start with the minimal required similarity.
495  $minPercentage = Config::getVar('files', 'filename_revision_match', 70);
496 
497  // Find out whether one of the files belonging to the current
498  // file stage matches the given file name.
499  $possibleRevisedFileId = null;
500  $matchedPercentage = 0;
501  foreach ((array) $submissionFiles as $submissionFile) { /* @var $submissionFile SubmissionFile */
502  // Test whether the current submission file is similar
503  // to the uploaded file. (Transliterate to ASCII -- the
504  // similar_text function can't handle UTF-8.)
505  similar_text(
506  $a = Stringy\Stringy::create($uploadedFileName)->toAscii(),
507  $b = Stringy\Stringy::create($submissionFile->getOriginalFileName())->toAscii(),
508  $matchedPercentage
509  );
510  if($matchedPercentage > $minPercentage && !$this->_onlyNumbersDiffer($a, $b)) {
511  // We found a file that might be a possible revision.
512  $possibleRevisedFileId = $submissionFile->getFileId();
513 
514  // Reset the min percentage to this comparison's precentage
515  // so that only better matches will be considered from now on.
516  $minPercentage = $matchedPercentage;
517  }
518  }
519 
520  // Return the id of the file that we found similar.
521  return $possibleRevisedFileId;
522  }
523 
532  function _onlyNumbersDiffer($a, $b) {
533  if ($a == $b) return false;
534 
535  $pattern = '/([^0-9]*)([0-9]*)([^0-9]*)/';
536  $aMatchCount = preg_match_all($pattern, $a, $aMatches, PREG_SET_ORDER);
537  $bMatchCount = preg_match_all($pattern, $b, $bMatches, PREG_SET_ORDER);
538  if ($aMatchCount != $bMatchCount || $aMatchCount == 0) return false;
539 
540  // Check each match. If the 1st and 3rd (text) parts all match
541  // then only numbers differ in the two supplied strings.
542  for ($i=0; $i<count($aMatches); $i++) {
543  if ($aMatches[$i][1] != $bMatches[$i][1]) return false;
544  if ($aMatches[$i][3] != $bMatches[$i][3]) return false;
545  }
546 
547  // No counterexamples were found. Only numbers differ.
548  return true;
549  }
550 
557  function _getUploadedFileInfo($uploadedFile) {
558  return array(
559  'uploadedFile' => array(
560  'fileId' => $uploadedFile->getFileId(),
561  'revision' => $uploadedFile->getRevision(),
562  'name' => $uploadedFile->getLocalizedName(),
563  'fileLabel' => $uploadedFile->getFileLabel(),
564  'type' => $uploadedFile->getDocumentType(),
565  'genreId' => $uploadedFile->getGenreId(),
566  )
567  );
568  }
569 }
PKPFileUploadWizardHandler\editMetadata
editMetadata($args, $request)
Definition: PKPFileUploadWizardHandler.inc.php:460
PKPHandler\addRoleAssignment
addRoleAssignment($roleIds, $operations)
Definition: PKPHandler.inc.php:213
PKPFileUploadWizardHandler\startWizard
startWizard($args, $request)
Definition: PKPFileUploadWizardHandler.inc.php:216
PKPFileUploadWizardHandler\initialize
initialize($request)
Definition: PKPFileUploadWizardHandler.inc.php:90
PKPFileUploadWizardHandler\_getMetadataForm
_getMetadataForm($request)
Definition: PKPFileUploadWizardHandler.inc.php:497
AppLocale\requireComponents
static requireComponents()
Definition: env1/MockAppLocale.inc.php:56
PKPFileUploadWizardHandler\$_reviewRound
$_reviewRound
Definition: PKPFileUploadWizardHandler.inc.php:46
PKPFileUploadWizardHandler\_getUploadedFileInfo
_getUploadedFileInfo($uploadedFile)
Definition: PKPFileUploadWizardHandler.inc.php:578
PKPFileUploadWizardHandler
Definition: PKPFileUploadWizardHandler.inc.php:23
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
PKPFileUploadWizardHandler\displayFileUploadForm
displayFileUploadForm($args, $request)
Definition: PKPFileUploadWizardHandler.inc.php:241
PKPFileUploadWizardHandler\$_assocType
$_assocType
Definition: PKPFileUploadWizardHandler.inc.php:58
SubmissionMailTemplate
Subclass of MailTemplate for sending emails related to submissions.
Definition: SubmissionMailTemplate.inc.php:20
PKPFileUploadWizardHandler\getAssocType
getAssocType()
Definition: PKPFileUploadWizardHandler.inc.php:195
PKPFileUploadWizardHandler\getAssocId
getAssocId()
Definition: PKPFileUploadWizardHandler.inc.php:203
PKPFileUploadWizardHandler\$_revisedFileId
$_revisedFileId
Definition: PKPFileUploadWizardHandler.inc.php:52
PKPHandler\getId
getId()
Definition: PKPHandler.inc.php:107
PKPFileUploadWizardHandler\getUploaderRoles
getUploaderRoles()
Definition: PKPFileUploadWizardHandler.inc.php:163
PKPFileUploadWizardHandler\_checkForRevision
& _checkForRevision(&$uploadedFile, &$submissionFiles)
Definition: PKPFileUploadWizardHandler.inc.php:511
PKPFileUploadWizardHandler\finishFileSubmission
finishFileSubmission($args, $request)
Definition: PKPFileUploadWizardHandler.inc.php:472
JSONMessage
Class to represent a JSON (Javascript Object Notation) message.
Definition: JSONMessage.inc.php:18
Config\getVar
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
PKPFileUploadWizardHandler\_attachEntities
_attachEntities($submissionFile)
Definition: PKPFileUploadWizardHandler.inc.php:313
PKPFileUploadWizardHandler\getSubmission
getSubmission()
Definition: PKPFileUploadWizardHandler.inc.php:136
Seboettg\Collection\count
count()
Definition: ArrayListTrait.php:253
PKPFileUploadWizardHandler\getStageId
getStageId()
Definition: PKPFileUploadWizardHandler.inc.php:145
PKPTemplateManager\getManager
static & getManager($request=null)
Definition: PKPTemplateManager.inc.php:1239
PKPFileUploadWizardHandler\__construct
__construct()
Definition: PKPFileUploadWizardHandler.inc.php:70
SubmissionFilesUploadConfirmationForm
Form for adding/editing a submission file.
Definition: SubmissionFilesUploadConfirmationForm.inc.php:19
SubmissionFilesUploadForm
Form for adding/editing a submission file.
Definition: SubmissionFilesUploadForm.inc.php:19
PKPFileUploadWizardHandler\$_assocId
$_assocId
Definition: PKPFileUploadWizardHandler.inc.php:64
PKPHandler\getAuthorizedContextObject
& getAuthorizedContextObject($assocType)
Definition: PKPHandler.inc.php:174
PKPFileUploadWizardHandler\uploadFile
uploadFile($args, $request)
Definition: PKPFileUploadWizardHandler.inc.php:262
PKPFileUploadWizardHandler\$_fileStage
$_fileStage
Definition: PKPFileUploadWizardHandler.inc.php:28
PKPFileUploadWizardHandler\getRevisedFileId
getRevisedFileId()
Definition: PKPFileUploadWizardHandler.inc.php:187
NotificationManager
Definition: NotificationManager.inc.php:19
PKPFileUploadWizardHandler\getReviewRound
getReviewRound()
Definition: PKPFileUploadWizardHandler.inc.php:179
PKPApplication\get
static get()
Definition: PKPApplication.inc.php:235
fatalError
if(!function_exists('import')) fatalError($reason)
Definition: functions.inc.php:32
PKPFileUploadWizardHandler\_onlyNumbersDiffer
_onlyNumbersDiffer($a, $b)
Definition: PKPFileUploadWizardHandler.inc.php:553
Handler
Base request handler application class.
Definition: Handler.inc.php:18
PKPFileUploadWizardHandler\$_revisionOnly
$_revisionOnly
Definition: PKPFileUploadWizardHandler.inc.php:40
PKPFileUploadWizardHandler\getFileStage
getFileStage()
Definition: PKPFileUploadWizardHandler.inc.php:155
PKPFileUploadWizardHandler\getRevisionOnly
getRevisionOnly()
Definition: PKPFileUploadWizardHandler.inc.php:171
PKPFileUploadWizardHandler\$_uploaderRoles
$_uploaderRoles
Definition: PKPFileUploadWizardHandler.inc.php:34
PKPFileUploadWizardHandler\confirmRevision
confirmRevision($args, $request)
Definition: PKPFileUploadWizardHandler.inc.php:425