207 lines
7.5 KiB
PHP
207 lines
7.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @file jobs/email/EditorialReminder.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 EditorialReminder
|
|
*
|
|
* @ingroup jobs
|
|
*
|
|
* @brief Class to handle a job to send an editorial reminder
|
|
*/
|
|
|
|
namespace PKP\jobs\email;
|
|
|
|
use APP\core\Application;
|
|
use APP\core\Services;
|
|
use APP\facades\Repo;
|
|
use APP\notification\Notification;
|
|
use APP\notification\NotificationManager;
|
|
use APP\submission\Submission;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use PKP\context\Context;
|
|
use PKP\db\DAORegistry;
|
|
use PKP\facades\Locale;
|
|
use PKP\jobs\BaseJob;
|
|
use PKP\mail\mailables\EditorialReminder as MailablesEditorialReminder;
|
|
use PKP\notification\NotificationSubscriptionSettingsDAO;
|
|
use PKP\notification\PKPNotification;
|
|
use PKP\submission\reviewRound\ReviewRound;
|
|
use PKP\submission\reviewRound\ReviewRoundDAO;
|
|
use PKP\user\User;
|
|
use PKP\workflow\WorkflowStageDAO;
|
|
|
|
class EditorialReminder extends BaseJob
|
|
{
|
|
protected int $editorId;
|
|
protected int $contextId;
|
|
|
|
public function __construct(int $editorId, int $contextId)
|
|
{
|
|
parent::__construct();
|
|
|
|
$this->editorId = $editorId;
|
|
$this->contextId = $contextId;
|
|
}
|
|
|
|
/**
|
|
* Execute the job.
|
|
*/
|
|
public function handle(): void
|
|
{
|
|
if (!$this->isSubscribed()) {
|
|
return;
|
|
}
|
|
|
|
/** @var Context $context */
|
|
$context = Services::get('context')->get($this->contextId);
|
|
$editor = Repo::user()->get($this->editorId);
|
|
|
|
// Don't use the request locale because this job is
|
|
// run during a scheduled task
|
|
$requestLocale = Locale::getLocale();
|
|
Locale::setLocale($this->getLocale($editor, $context));
|
|
|
|
$submissionIds = Repo::submission()
|
|
->getCollector()
|
|
->assignedTo([$this->editorId])
|
|
->filterByContextIds([$this->contextId])
|
|
->filterByStatus([Submission::STATUS_QUEUED])
|
|
->filterByIncomplete(false)
|
|
->getIds();
|
|
|
|
$outstanding = [];
|
|
$submissions = [];
|
|
|
|
/** @var int $submissionId */
|
|
foreach ($submissionIds as $submissionId) {
|
|
$submission = Repo::submission()->get($submissionId);
|
|
$submissions[$submissionId] = $submission;
|
|
|
|
if ($submission->getData('stageId') == WORKFLOW_STAGE_ID_SUBMISSION) {
|
|
$outstanding[$submissionId] = __('editor.submission.status.waitingInitialReview');
|
|
continue;
|
|
}
|
|
|
|
if (in_array($submission->getData('stageId'), [WORKFLOW_STAGE_ID_INTERNAL_REVIEW, WORKFLOW_STAGE_ID_EXTERNAL_REVIEW])) {
|
|
/** @var ReviewRoundDAO $reviewRoundDao */
|
|
$reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO');
|
|
$reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $submission->getData('stageId'));
|
|
|
|
if ($reviewRound->getStatus() === ReviewRound::REVIEW_ROUND_STATUS_PENDING_REVIEWERS) {
|
|
$outstanding[$submissionId] = __('editor.submission.roundStatus.pendingReviewers');
|
|
continue;
|
|
}
|
|
|
|
if ($reviewRound->getStatus() === ReviewRound::REVIEW_ROUND_STATUS_REVIEWS_COMPLETED) {
|
|
$outstanding[$submissionId] = __('editor.submission.roundStatus.reviewsCompleted');
|
|
continue;
|
|
}
|
|
|
|
if ($reviewRound->getStatus() === ReviewRound::REVIEW_ROUND_STATUS_REVIEWS_OVERDUE) {
|
|
$outstanding[$submissionId] = __('editor.submission.roundStatus.reviewOverdue');
|
|
continue;
|
|
}
|
|
|
|
if ($reviewRound->getStatus() === ReviewRound::REVIEW_ROUND_STATUS_REVISIONS_SUBMITTED) {
|
|
$outstanding[$submissionId] = __('editor.submission.roundStatus.revisionsSubmitted');
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (in_array($submission->getData('stageId'), [WORKFLOW_STAGE_ID_EDITING, WORKFLOW_STAGE_ID_PRODUCTION])) {
|
|
$lastActivityTimestamp = strtotime($submission->getData('dateLastActivity'));
|
|
if ($lastActivityTimestamp < strtotime('+30 days')) {
|
|
/** @var WorkflowStageDAO $workflowStageDao */
|
|
$workflowStageDao = DAORegistry::getDAO('WorkflowStageDAO');
|
|
$outstanding[$submissionId] = __(
|
|
'editor.submission.status.inactiveDaysInStage',
|
|
[
|
|
'days' => 30,
|
|
'stage' => __($workflowStageDao->getTranslationKeyFromId($submission->getData('stageId')))
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
if (count($outstanding) > 20) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (empty($outstanding)) {
|
|
return;
|
|
}
|
|
|
|
// Context or user was removed since job was created, or the user was disabled
|
|
if (!$context || !$editor) {
|
|
return;
|
|
}
|
|
|
|
$notificationManager = new NotificationManager();
|
|
$notification = $notificationManager->createNotification(
|
|
Application::get()->getRequest(),
|
|
$this->editorId,
|
|
PKPNotification::NOTIFICATION_TYPE_EDITORIAL_REMINDER,
|
|
$this->contextId
|
|
);
|
|
|
|
$mailable = new MailablesEditorialReminder($context);
|
|
$emailTemplate = Repo::emailTemplate()->getByKey($context->getId(), $mailable::getEmailTemplateKey());
|
|
|
|
$mailable
|
|
->setOutstandingTasks($outstanding, $submissions, $submissionIds->count())
|
|
->from($context->getContactEmail(), $context->getLocalizedName(Locale::getLocale()))
|
|
->recipients([$editor])
|
|
->subject($emailTemplate->getLocalizedData('subject'))
|
|
->body($emailTemplate->getLocalizedData('body'))
|
|
->allowUnsubscribe($notification);
|
|
|
|
Mail::send($mailable);
|
|
|
|
// Restore the current locale after the email is sent
|
|
Locale::setLocale($requestLocale);
|
|
}
|
|
|
|
/**
|
|
* Is this editor subscribed to this email?
|
|
*/
|
|
protected function isSubscribed(): bool
|
|
{
|
|
/** @var NotificationSubscriptionSettingsDAO $notificationSubscriptionSettingsDao */
|
|
$notificationSubscriptionSettingsDao = DAORegistry::getDAO('NotificationSubscriptionSettingsDAO');
|
|
$blockedEmails = $notificationSubscriptionSettingsDao->getNotificationSubscriptionSettings('blocked_emailed_notification', $this->editorId, $this->contextId);
|
|
return !in_array(Notification::NOTIFICATION_TYPE_EDITORIAL_REMINDER, $blockedEmails);
|
|
}
|
|
|
|
/**
|
|
* Get the locale to use with this email
|
|
*
|
|
* Returns the context's primary locale, or the first locale
|
|
* supported by the context and the user.
|
|
*
|
|
* @return string Locale key. Example: en
|
|
*/
|
|
protected function getLocale(User $editor, Context $context): string
|
|
{
|
|
$locale = $context->getPrimaryLocale();
|
|
|
|
// A user's locales may not be an array due to bug with data structure
|
|
// See: https://github.com/pkp/pkp-lib/issues/8023
|
|
if ($editor->getLocales() === false) {
|
|
return $locale;
|
|
}
|
|
|
|
$locales = array_intersect($editor->getLocales(), $context->getSupportedLocales());
|
|
if (!empty($locales) && !in_array($context->getPrimaryLocale(), $locales)) {
|
|
$locale = $locales[0];
|
|
}
|
|
|
|
return $locale;
|
|
}
|
|
}
|