diff --git a/api/v1/submissions/PKPSubmissionHandler.php b/api/v1/submissions/PKPSubmissionHandler.php index 8d2a9c6fff2..fd78cd48e13 100644 --- a/api/v1/submissions/PKPSubmissionHandler.php +++ b/api/v1/submissions/PKPSubmissionHandler.php @@ -21,7 +21,7 @@ use APP\core\Request; use APP\core\Services; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; +use APP\log\event\SubmissionEventLogEntry; use APP\notification\Notification; use APP\notification\NotificationManager; use APP\section\Section; @@ -31,11 +31,12 @@ use Illuminate\Support\Facades\Mail; use PKP\core\APIResponse; use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; use PKP\decision\DecisionType; use PKP\facades\Locale; use PKP\handler\APIHandler; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\mailables\PublicationVersionNotify; use PKP\mail\mailables\SubmissionSavedForLater; use PKP\notification\NotificationSubscriptionSettingsDAO; @@ -48,6 +49,7 @@ use PKP\security\authorization\SubmissionAccessPolicy; use PKP\security\authorization\UserRolesRequiredPolicy; use PKP\security\Role; +use PKP\security\Validation; use PKP\services\PKPSchemaService; use PKP\stageAssignment\StageAssignmentDAO; use PKP\submission\GenreDAO; @@ -709,17 +711,21 @@ public function submit(SlimRequest $slimRequest, APIResponse $response, array $a $submission = Repo::submission()->get($submission->getId()); if ($slimRequest->getParsedBodyParam('confirmCopyright')) { - SubmissionLog::logEvent( - Application::get()->getRequest(), - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_COPYRIGHT_AGREED, - 'submission.event.copyrightAgreed', - [ - 'username' => $request->getUser()->getUsername(), - 'name' => $request->getUser()->getFullName(true, false, Locale::getLocale()), - 'copyrightNotice' => $context->getLocalizedData('copyrightNotice', Locale::getLocale()), - ] - ); + $user = $request->getUser(); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_COPYRIGHT_AGREED, + 'userId' => Validation::loggedInAs() ?? $user->getId(), + 'message' => 'submission.event.copyrightAgreed', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'username' => $user->getUsername(), + 'userFullName' => $user->getFullName(true, false, Locale::getLocale()), + 'copyrightNotice' => $context->getData('copyrightNotice'), + ]); + + Repo::eventLog()->add($eventLog); } $userGroups = Repo::userGroup() diff --git a/classes/announcement/Repository.php b/classes/announcement/Repository.php index b3a00507be6..b2a86ce3920 100644 --- a/classes/announcement/Repository.php +++ b/classes/announcement/Repository.php @@ -99,7 +99,7 @@ public function validate(?Announcement $object, array $props, array $allowedLoca ] ); - // Check required fields if we're adding a context + // Check required fields ValidatorFactory::required( $validator, $object, diff --git a/classes/category/Repository.php b/classes/category/Repository.php index 082887196a5..270718eb82b 100644 --- a/classes/category/Repository.php +++ b/classes/category/Repository.php @@ -108,9 +108,9 @@ public function getSchemaMap(): maps\Schema } /** - * Validate properties for an announcement + * Validate properties for a category * - * Perform validation checks on data used to add or edit an announcement. + * Perform validation checks on data used to add or edit a category. * * @param array $props A key/value array with the new data to validate * @param array $allowedLocales The context's supported locales @@ -126,7 +126,7 @@ public function validate(?Category $object, array $props, array $allowedLocales, [] ); - // Check required fields if we're adding a context + // Check required fields ValidatorFactory::required( $validator, $object, diff --git a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php index 1d4b2211bb7..4338c2c2f3f 100644 --- a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php +++ b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php @@ -20,7 +20,7 @@ use APP\core\PageRouter; use APP\core\Request; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; +use APP\log\event\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use APP\template\TemplateManager; @@ -46,6 +46,7 @@ use PKP\facades\Locale; use PKP\linkAction\LinkAction; use PKP\linkAction\request\AjaxModal; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\log\SubmissionLog; use PKP\mail\Mailable; use PKP\mail\mailables\ReviewerReinstate; @@ -62,6 +63,7 @@ use PKP\security\authorization\internal\ReviewRoundRequiredPolicy; use PKP\security\authorization\WorkflowStageAccessPolicy; use PKP\security\Role; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use PKP\submission\reviewRound\ReviewRound; @@ -682,17 +684,19 @@ public function unconsiderReview($args, $request) $reviewAssignmentDao->updateObject($reviewAssignment); // log the unconsider. - SubmissionLog::logEvent( - $request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_UNCONSIDERED, - 'log.review.reviewUnconsidered', - [ - 'editorName' => $user->getFullName(), - 'submissionId' => $submission->getId(), - 'round' => $reviewAssignment->getRound(), - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_UNCONSIDERED, + 'userId' => Validation::loggedInAs() ?? $user->getId(), + 'message' => 'log.review.reviewUnconsidered', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'editorName' => $user->getFullName(), + 'submissionId' => $submission->getId(), + 'round' => $reviewAssignment->getRound(), + ]); + Repo::eventLog()->add($eventLog); return DAO::getDataChangedEvent($reviewAssignment->getId()); } @@ -745,17 +749,19 @@ public function reviewRead($args, $request) $submissionId = $reviewAssignment->getSubmissionId(); $submission = Repo::submission()->get($submissionId); - SubmissionLog::logEvent( - $request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_CONFIRMED, - 'log.review.reviewConfirmed', - [ - 'userName' => $user->getFullName(), - 'submissionId' => $reviewAssignment->getSubmissionId(), - 'round' => $reviewAssignment->getRound() - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_CONFIRMED, + 'userId' => Validation::loggedInAs() ?? $user->getId(), + 'message' => 'log.review.reviewConfirmed', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'editorName' => $user->getFullName(), + 'submissionId' => $reviewAssignment->getSubmissionId(), + 'round' => $reviewAssignment->getRound() + ]); + Repo::eventLog()->add($eventLog); } // Remove the reviewer task. $notificationDao = DAORegistry::getDAO('NotificationDAO'); /** @var NotificationDAO $notificationDao */ diff --git a/classes/core/PKPApplication.php b/classes/core/PKPApplication.php index 8fb66e16b6f..0f9b47e25f1 100644 --- a/classes/core/PKPApplication.php +++ b/classes/core/PKPApplication.php @@ -506,8 +506,6 @@ public function getDAOMap() 'SubmissionDisciplineDAO' => 'PKP\submission\SubmissionDisciplineDAO', 'SubmissionDisciplineEntryDAO' => 'PKP\submission\SubmissionDisciplineEntryDAO', 'SubmissionEmailLogDAO' => 'PKP\log\SubmissionEmailLogDAO', - 'SubmissionEventLogDAO' => 'PKP\log\SubmissionEventLogDAO', - 'SubmissionFileEventLogDAO' => 'PKP\log\SubmissionFileEventLogDAO', 'QueryDAO' => 'PKP\query\QueryDAO', 'SubmissionLanguageDAO' => 'PKP\submission\SubmissionLanguageDAO', 'SubmissionLanguageEntryDAO' => 'PKP\submission\SubmissionLanguageEntryDAO', diff --git a/classes/db/DBDataXMLParser.php b/classes/db/DBDataXMLParser.php index c797b8ef9f2..95042036b1e 100644 --- a/classes/db/DBDataXMLParser.php +++ b/classes/db/DBDataXMLParser.php @@ -99,7 +99,7 @@ public function parseData($file) if ($column) { $this->sql = array_merge($this->sql, array_column(DB::pretend(function () use ($table, $column) { Schema::table($table, function (Blueprint $table) use ($column) { - $table->dropColumn('column'); + $table->dropColumn($column); }); }), 'query')); } else { diff --git a/classes/decision/Repository.php b/classes/decision/Repository.php index 1c7bb2a955f..3185f843b1d 100644 --- a/classes/decision/Repository.php +++ b/classes/decision/Repository.php @@ -26,12 +26,14 @@ use Illuminate\Support\Facades\App; use PKP\context\Context; use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; -use PKP\log\PKPSubmissionEventLogEntry; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\log\SubmissionLog; use PKP\observers\events\DecisionAdded; use PKP\plugins\Hook; use PKP\security\Role; +use PKP\security\Validation; use PKP\services\PKPSchemaService; use PKP\stageAssignment\StageAssignment; use PKP\stageAssignment\StageAssignmentDAO; @@ -228,20 +230,18 @@ public function add(Decision $decision): int } // Log the decision - SubmissionLog::logEvent( - $this->request, - $submission, - $this->isRecommendation($decisionType->getDecision()) + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => $this->isRecommendation($decisionType->getDecision()) ? PKPSubmissionEventLogEntry::SUBMISSION_LOG_EDITOR_RECOMMENDATION : PKPSubmissionEventLogEntry::SUBMISSION_LOG_EDITOR_DECISION, - $decisionType->getLog(), - [ - 'editorId' => $editor->getId(), - 'editorName' => $editor->getFullName(), - 'submissionId' => $decision->getData('submissionId'), - 'decision' => $decisionType->getDecision(), - ] - ); + 'userId' => Validation::loggedInAs() ?? $this->request->getUser()?->getId(), + 'message' => $decisionType->getLog(), + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); // Allow the decision type to perform additional actions $decisionType->runAdditionalActions($decision, $submission, $editor, $context, $actions); diff --git a/classes/decision/types/traits/NotifyReviewers.php b/classes/decision/types/traits/NotifyReviewers.php index f1d8aed53ed..124e11a2fa6 100644 --- a/classes/decision/types/traits/NotifyReviewers.php +++ b/classes/decision/types/traits/NotifyReviewers.php @@ -15,16 +15,19 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; +use APP\log\event\SubmissionEventLogEntry; use APP\submission\Submission; use Illuminate\Support\Facades\Mail; use Illuminate\Validation\Validator; use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\log\SubmissionLog; use PKP\mail\EmailData; use PKP\mail\mailables\DecisionNotifyReviewer; use PKP\mail\mailables\ReviewerUnassign; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use PKP\user\User; @@ -62,16 +65,18 @@ protected function sendReviewersEmail(DecisionNotifyReviewer|ReviewerUnassign $m } } - SubmissionLog::logEvent( - Application::get()->getRequest(), - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_DECISION_EMAIL_SENT, - 'submission.event.decisionReviewerEmailSent', - [ - 'recipientCount' => count($recipients), - 'subject' => $email->subject, - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_DECISION_EMAIL_SENT, + 'userId' => Validation::loggedInAs() ?? Application::get()->getRequest()->getUser()?->getId(), + 'message' => 'submission.event.decisionReviewerEmailSent', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'recipientCount' => count($recipients), + 'subject' => $email->subject, + ]); + Repo::eventLog()->add($eventLog); } /** diff --git a/classes/facades/Repo.php b/classes/facades/Repo.php index 1ce397c88d2..9e443678a4d 100644 --- a/classes/facades/Repo.php +++ b/classes/facades/Repo.php @@ -34,6 +34,7 @@ use PKP\job\repositories\Job as JobRepository; use PKP\submissionFile\Repository as SubmissionFileRepository; use PKP\userGroup\Repository as UserGroupRepository; +use PKP\log\event\Repository as EventLogRepository; class Repo { @@ -86,4 +87,9 @@ public static function userGroup(): UserGroupRepository { return app(UserGroupRepository::class); } + + public static function eventLog(): EventLogRepository + { + return app(EventLogRepository::class); + } } diff --git a/classes/galley/Repository.php b/classes/galley/Repository.php index add409bfcd9..a43a59d57e6 100644 --- a/classes/galley/Repository.php +++ b/classes/galley/Repository.php @@ -129,7 +129,7 @@ public function validate(?Galley $object, array $props, array $allowedLocales, s ] ); - // Check required fields if we're adding a context + // Check required fields ValidatorFactory::required( $validator, $object, diff --git a/classes/log/EventLogDAO.php b/classes/log/EventLogDAO.php deleted file mode 100644 index 192ef73d862..00000000000 --- a/classes/log/EventLogDAO.php +++ /dev/null @@ -1,212 +0,0 @@ -retrieve( - 'SELECT * FROM event_log WHERE log_id = ?' . - (isset($assocType) ? ' AND assoc_type = ? AND assoc_id = ?' : ''), - $params - ); - $row = $result->current(); - return $row ? $this->build((array) $row) : null; - } - - /** - * Retrieve all log entries matching the specified association. - * - * @param int $assocType - * @param int $assocId - * @param ?\PKP\db\DBResultRange $rangeInfo optional - * - * @return DAOResultFactory containing matching EventLogEntry ordered by sequence - */ - public function getByAssoc($assocType, $assocId, $rangeInfo = null) - { - $result = $this->retrieveRange( - 'SELECT * FROM event_log WHERE assoc_type = ? AND assoc_id = ? ORDER BY log_id DESC', - [(int) $assocType, (int) $assocId], - $rangeInfo - ); - - return new DAOResultFactory($result, $this, 'build'); - } - - /** - * Instantiate a new data object - * - * @return object - */ - public function newDataObject() - { - return new EventLogEntry(); - } - - /** - * Internal function to return an EventLogEntry object from a row. - * - * @param array $row - * - * @return EventLogEntry - */ - public function build($row) - { - $entry = $this->newDataObject(); - $entry->setId($row['log_id']); - $entry->setUserId($row['user_id']); - $entry->setDateLogged($this->datetimeFromDB($row['date_logged'])); - $entry->setEventType($row['event_type']); - $entry->setAssocType($row['assoc_type']); - $entry->setAssocId($row['assoc_id']); - $entry->setMessage($row['message']); - $entry->setIsTranslated($row['is_translated']); - - $result = $this->retrieve('SELECT * FROM event_log_settings WHERE log_id = ?', [(int) $entry->getId()]); - $params = []; - foreach ($result as $r) { - $params[$r->setting_name] = $this->convertFromDB( - $r->setting_value, - $r->setting_type - ); - } - $entry->setParams($params); - - Hook::call('EventLogDAO::build', [&$entry, &$row]); - - return $entry; - } - - /** - * Insert a new log entry. - * - * @param EventLogEntry $entry - */ - public function insertObject($entry) - { - $this->update( - sprintf( - 'INSERT INTO event_log - (user_id, date_logged, event_type, assoc_type, assoc_id, message, is_translated) - VALUES - (?, %s, ?, ?, ?, ?, ?)', - $this->datetimeToDB($entry->getDateLogged()) - ), - [ - (int) $entry->getUserId(), - (int) $entry->getEventType(), - (int) $entry->getAssocType(), - (int) $entry->getAssocId(), - $entry->getMessage(), - (int) $entry->getIsTranslated() - ] - ); - $entry->setId($this->getInsertId()); - - // Add name => value entries into the settings table - $params = $entry->getParams(); - if (is_array($params)) { - foreach ($params as $key => $value) { - $type = null; - $value = $this->convertToDB($value, $type); - $this->update( - 'INSERT INTO event_log_settings (log_id, setting_name, setting_value, setting_type) VALUES (?, ?, ?, ?)', - [(int) $entry->getId(), $key, $value, $type] - ); - } - } - - return $entry->getId(); - } - - /** - * Delete a single log entry (and associated settings). - * - * @param int $logId - * @param null|mixed $assocType - * @param null|mixed $assocId - */ - public function deleteById($logId, $assocType = null, $assocId = null) - { - $params = [(int) $logId]; - if ($assocType !== null) { - $params[] = (int) $assocType; - $params[] = (int) $assocId; - } - if ($this->update( - 'DELETE FROM event_log WHERE log_id = ?' . - ($assocType !== null ? ' AND assoc_type = ? AND assoc_id = ?' : ''), - $params - )) { - $this->update('DELETE FROM event_log_settings WHERE log_id = ?', [(int) $logId]); - } - } - - /** - * Delete all log entries for an object. - * - * @param int $assocType - * @param int $assocId - */ - public function deleteByAssoc($assocType, $assocId) - { - $entries = $this->getByAssoc($assocType, $assocId); - while ($entry = $entries->next()) { - $this->deleteById($entry->getId()); - } - } - - /** - * Transfer all log entries to another user. - * - * @param int $oldUserId - * @param int $newUserId - */ - public function changeUser($oldUserId, $newUserId) - { - $this->update( - 'UPDATE event_log SET user_id = ? WHERE user_id = ?', - [(int) $newUserId, (int) $oldUserId] - ); - } -} - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\log\EventLogDAO', '\EventLogDAO'); -} diff --git a/classes/log/SubmissionEventLogDAO.php b/classes/log/SubmissionEventLogDAO.php deleted file mode 100644 index dbc6d48d067..00000000000 --- a/classes/log/SubmissionEventLogDAO.php +++ /dev/null @@ -1,53 +0,0 @@ -setAssocType(Application::ASSOC_TYPE_SUBMISSION); - return $returner; - } - - /** - * Get submission event log entries by submission ID - * - * @param int $submissionId - * - * @return \PKP\db\DAOResultFactory - */ - public function getBySubmissionId($submissionId) - { - return $this->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId); - } -} - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\log\SubmissionEventLogDAO', '\SubmissionEventLogDAO'); -} diff --git a/classes/log/SubmissionFileEventLogDAO.php b/classes/log/SubmissionFileEventLogDAO.php deleted file mode 100644 index bc2267313b8..00000000000 --- a/classes/log/SubmissionFileEventLogDAO.php +++ /dev/null @@ -1,52 +0,0 @@ -setAssocType(Application::ASSOC_TYPE_SUBMISSION_FILE); - return $returner; - } - - /** - * Get event log entries by submission file ID. - * - * @param int $submissionFileId - * - * @return \PKP\db\DAOResultFactory - */ - public function getBySubmissionFileId($submissionFileId) - { - return $this->getByAssoc(Application::ASSOC_TYPE_SUBMISSION_FILE, $submissionFileId); - } -} - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\log\SubmissionFileEventLogDAO', '\SubmissionFileEventLogDAO'); -} diff --git a/classes/log/SubmissionFileLog.php b/classes/log/SubmissionFileLog.php deleted file mode 100644 index 8b3f83ce69b..00000000000 --- a/classes/log/SubmissionFileLog.php +++ /dev/null @@ -1,67 +0,0 @@ -newDataObject(); - - // Set implicit parts of the log entry - $entry->setDateLogged(Core::getCurrentDate()); - - $user = $request->getUser(); - if ($user) { - $entry->setUserId($user->getId()); - } - - $entry->setAssocType(Application::ASSOC_TYPE_SUBMISSION_FILE); - $entry->setAssocId($submissionFile->getId()); - - // Set explicit parts of the log entry - $entry->setEventType($eventType); - $entry->setMessage($messageKey); - $entry->setParams($params); - $entry->setIsTranslated(0); // Legacy for other apps. All messages use locale keys. - - // Insert the resulting object - $submissionFileEventLogDao->insertObject($entry); - return $entry; - } -} - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\log\SubmissionFileLog', '\SubmissionFileLog'); -} diff --git a/classes/log/SubmissionLog.php b/classes/log/SubmissionLog.php deleted file mode 100644 index 35e0daac0be..00000000000 --- a/classes/log/SubmissionLog.php +++ /dev/null @@ -1,88 +0,0 @@ -newDataObject(); - - // Set implicit parts of the log entry - $entry->setDateLogged(Core::getCurrentDate()); - - if (Validation::isLoggedInAs()) { - // If user is logged in as another user log with real userid - $sessionManager = SessionManager::getManager(); - $session = $sessionManager->getUserSession(); - $userId = $session->getSessionVar('signedInAs'); - if ($userId) { - $entry->setUserId($userId); - } - } else { - $user = $request->getUser(); - if ($user) { - $entry->setUserId($user->getId()); - } - } - - $entry->setSubmissionId($submission->getId()); - - // Set explicit parts of the log entry - $entry->setEventType($eventType); - $entry->setMessage($messageKey); - $entry->setParams($params); - $entry->setIsTranslated(0); // Legacy for old entries. All messages now use locale keys. - - // Insert the resulting object - $submissionEventLogDao->insertObject($entry); - - // Stamp the submission status modification date without triggering edit event - $submission->stampLastActivity(); - Repo::submission()->dao->update($submission); - - return $entry; - } -} - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\log\SubmissionLog', '\SubmissionLog'); -} diff --git a/classes/log/event/Collector.php b/classes/log/event/Collector.php new file mode 100644 index 00000000000..3b1184a2e82 --- /dev/null +++ b/classes/log/event/Collector.php @@ -0,0 +1,110 @@ +dao = $dao; + } + + public function getCount(): int + { + return $this->dao->getCount($this); + } + + public function getIds(): Collection + { + return $this->dao->getIds($this); + } + + public function getMany(): LazyCollection + { + return $this->dao->getMany($this); + } + + /** + * + * @param null|int $assocType One of the Application::ASSOC_TYPE_ constants + * @param null|array $assocIds Match for the specified assoc type + */ + public function filterByAssoc(?int $assocType, ?array $assocIds = null): self + { + $this->assocType = $assocType; + $this->assocIds = $assocIds; + return $this; + } + + public function filterByUserIds(?array $userIds): self + { + $this->userIds = $userIds; + return $this; + } + + public function limit(?int $count): self + { + $this->count = $count; + return $this; + } + + public function offset(?int $offset): self + { + $this->offset = $offset; + return $this; + } + + /** + * @inheritDoc + */ + public function getQueryBuilder(): Builder + { + $q = DB::table($this->dao->table . ' as e') + ->select(['e.*']) + ->when(!is_null($this->assocType), function (Builder $q) { + return $q->where('assoc_type', $this->assocType); + }) + ->when(!is_null($this->assocIds), function (Builder $q) { + return $q->whereIn('assoc_id', $this->assocIds); + }) + ->when(!is_null($this->userIds), function (Builder $q) { + return $q->whereIn('user_id', $this->userIds); + }) + ->orderBy('date_logged', 'desc') + ->when(!is_null($this->count), function (Builder $q) { + return $q->limit($this->count); + }) + ->when(!is_null($this->count) && !is_null($this->offset), function (Builder $q) { + return $q->offset($this->offset); + }); + + Hook::call('EventLog::Collector::getQueryBuilder', [&$q, $this]); + + return $q; + } +} \ No newline at end of file diff --git a/classes/log/event/DAO.php b/classes/log/event/DAO.php new file mode 100644 index 00000000000..b7ac90550e9 --- /dev/null +++ b/classes/log/event/DAO.php @@ -0,0 +1,169 @@ + 'log_id', + 'assocType' => 'assoc_type', + 'assocId' => 'assoc_id', + 'userId' => 'user_id', + 'dateLogged' => 'date_logged', + 'eventType' => 'event_type', + 'message' => 'message', + 'isTranslated' => 'is_translated', + ]; + + /** + * Instantiate a new DataObject + */ + public function newDataObject(): EventLogEntry + { + return app(EventLogEntry::class); + } + + /** + * Check if a log entry exists + */ + public function exists(int $id): bool + { + return DB::table($this->table) + ->where($this->primaryKeyColumn, '=', $id) + ->exists(); + } + + /** + * Get an event log entry + */ + public function get(int $id): ?EventLogEntry + { + $row = DB::table($this->table) + ->where($this->primaryKeyColumn, $id) + ->first(); + return $row ? $this->fromRow($row) : null; + } + + /** + * Get the number of log entries matching the configured query + */ + public function getCount(Collector $query): int + { + return $query + ->getQueryBuilder() + ->count(); + } + + /** + * Get a list of ids matching the configured query + */ + public function getIds(Collector $query): Collection + { + return $query + ->getQueryBuilder() + ->select('e.' . $this->primaryKeyColumn) + ->pluck('e.' . $this->primaryKeyColumn); + } + + /** + * Get a collection of log entries matching the configured query + */ + public function getMany(Collector $query): LazyCollection + { + $rows = $query + ->getQueryBuilder() + ->get(); + + return LazyCollection::make(function () use ($rows) { + foreach ($rows as $row) { + yield $row->log_id => $this->fromRow($row); + } + }); + } + + /** + * @copydoc EntityDAO::fromRow() + */ + public function fromRow(object $row): EventLogEntry + { + $logEntry = parent::fromRow($row); + $schema = $this->schemaService->get($this->schema); + + DB::table($this->settingsTable) + ->where($this->primaryKeyColumn, '=', $row->{$this->primaryKeyColumn}) + ->get() + ->each(function ($row) use ($logEntry, $schema) { + if (!empty($schema->properties->{$row->setting_name})) { + return; + } + + // Retrieve custom properties + if (!empty($row->setting_type)) { + $logEntry->setData( + $row->setting_name, + $this->convertFromDB( + $row->setting_value, + $row->setting_type + ), + empty($row->locale) ? null : $row->locale + ); + } + }); + + return $logEntry; + } + + /** + * @copydoc EntityDAO::insert() + */ + public function insert(EventLogEntry $eventLog): int + { + return parent::_insert($eventLog); + } + + /** + * @copydoc EntityDAO::delete() + */ + public function delete(EventLogEntry $eventLog) + { + parent::_delete($eventLog); + } + + /** + * Transfer all log entries to another user. + */ + public function changeUser(int $oldUserId, int $newUserId) + { + DB::table($this->table)->where('user_id', $oldUserId)->update(['user_id' => $newUserId]); + } +} \ No newline at end of file diff --git a/classes/log/EventLogEntry.php b/classes/log/event/EventLogEntry.php similarity index 98% rename from classes/log/EventLogEntry.php rename to classes/log/event/EventLogEntry.php index 4b99afb38ed..9ec1fef0313 100644 --- a/classes/log/EventLogEntry.php +++ b/classes/log/event/EventLogEntry.php @@ -1,7 +1,7 @@ dao = $dao; + $this->request = $request; + $this->schemaService = $schemaService; + } + + /** @copydoc DAO::newDataObject() */ + public function newDataObject(array $params = []): EventLogEntry + { + $object = $this->dao->newDataObject(); + if (!empty($params)) { + $object->setAllData($params); + } + return $object; + } + + /** @copydoc DAO::getByKey() */ + public function get(int $key): ?EventLogEntry + { + return $this->dao->get($key); + } + + /** @copydoc DAO::getCollector() */ + public function getCollector(): Collector + { + return app(Collector::class); + } + + /** + * Get an instance of the map class for mapping + * log entries to their schema + */ + public function getSchemaMap(): maps\Schema + { + return app('maps')->withExtensions($this->schemaMap); + } + + /** + * Validate properties for an event log entry + * + * Perform validation checks on data used to add or edit an event log entry. + * + * @param array $props A key/value array with the new data to validate + * @param array $allowedLocales The context's supported locales + * @param string $primaryLocale The context's primary locale + * + * @return array A key/value array with validation errors. Empty if no errors + */ + public function validate(?EventLogEntry $object, array $props, Context $context): array + { + $allowedLocales = $context->getSupportedSubmissionLocales();; + $primaryLocale = $context->getPrimaryLocale(); + + $validator = ValidatorFactory::make( + $props, + $this->schemaService->getValidationRules($this->dao->schema, $allowedLocales) + ); + + // Check required fields + ValidatorFactory::required( + $validator, + $object, + $this->schemaService->getRequiredProps($this->dao->schema), + $this->schemaService->getMultilingualProps($this->dao->schema), + $allowedLocales, + $primaryLocale + ); + + // Check for input from disallowed locales + ValidatorFactory::allowedLocales($validator, $this->schemaService->getMultilingualProps($this->dao->schema), $allowedLocales); + + $errors = []; + + if ($validator->fails()) { + $errors = $this->schemaService->formatValidationErrors($validator->errors()); + } + + Hook::call('EventLog::validate', [&$errors, $object, $props, $allowedLocales, $primaryLocale]); + + return $errors; + } + + /** @copydoc DAO::insert() */ + public function add(EventLogEntry $logEntry): int + { + $id = $this->dao->insert($logEntry); + Hook::call('EventLog::add', [$logEntry]); + + // Stamp the submission status modification date without triggering edit event + if ($logEntry->getData('assocType') === PKPApplication::ASSOC_TYPE_SUBMISSION) { + $submission = Repo::submission()->get($logEntry->getData('assocId')); + $submission->stampLastActivity(); + Repo::submission()->dao->update($submission); + } + + return $id; + } + + /** @copydoc DAO::delete() */ + public function delete(EventLogEntry $logEntry) + { + Hook::call('EventLog::delete::before', [$logEntry]); + $this->dao->delete($logEntry); + Hook::call('EventLog::delete', [$logEntry]); + } + + /** + * Delete a collection of categories + */ + public function deleteMany(Collector $collector) + { + foreach ($collector->getMany() as $logEntry) { + /** @var EventLogEntry $logEntry */ + $this->delete($logEntry); + } + } +} \ No newline at end of file diff --git a/classes/log/SubmissionFileEventLogEntry.php b/classes/log/event/SubmissionFileEventLogEntry.php similarity index 89% rename from classes/log/SubmissionFileEventLogEntry.php rename to classes/log/event/SubmissionFileEventLogEntry.php index 747c841145b..ec651a2abb0 100644 --- a/classes/log/SubmissionFileEventLogEntry.php +++ b/classes/log/event/SubmissionFileEventLogEntry.php @@ -1,7 +1,7 @@ mapByProperties($this->getProps(), $item); + } + + /** + * Summarize an event log + * + * Includes properties with the apiSummary flag in the event log entry schema. + */ + public function summarize(EventLogEntry $entry): array + { + return $this->mapByProperties($this->getSummaryProps(), $entry); + } + + /** + * Map a collection of event log entries + * + * @see self::map + */ + public function mapMany(Enumerable $collection): Enumerable + { + $this->collection = $collection; + return $collection->map(function ($category) { + return $this->map($category); + }); + } + + /** + * Summarize a collection of event log entries + * + * @see self::summarize + */ + public function summarizeMany(Enumerable $collection): Enumerable + { + $this->collection = $collection; + return $collection->map(function ($category) { + return $this->summarize($category); + }); + } + + /** + * Map schema properties of an event log entry to an assoc array + */ + protected function mapByProperties(array $props, EventLogEntry $entry): array + { + $output = []; + + foreach ($props as $prop) { + switch ($prop) { + default: + $output[$prop] = $entry->getData($prop); + break; + } + } + + return $output; + } +} diff --git a/classes/migration/install/LogMigration.php b/classes/migration/install/LogMigration.php index 8bdfccdb94e..76442b67fbd 100644 --- a/classes/migration/install/LogMigration.php +++ b/classes/migration/install/LogMigration.php @@ -31,14 +31,14 @@ public function up(): void $table->bigInteger('assoc_type'); $table->bigInteger('assoc_id'); - $table->bigInteger('user_id'); + $table->bigInteger('user_id')->nullable()->comment('NULL if it\'s system or automated event'); $table->foreign('user_id')->references('user_id')->on('users')->onDelete('cascade'); $table->index(['user_id'], 'event_log_user_id'); $table->datetime('date_logged'); $table->bigInteger('event_type')->nullable(); $table->text('message')->nullable(); - $table->smallInteger('is_translated')->nullable(); + $table->boolean('is_translated')->nullable(); $table->index(['assoc_type', 'assoc_id'], 'event_log_assoc'); }); @@ -49,10 +49,10 @@ public function up(): void $table->foreign('log_id', 'event_log_settings_log_id')->references('log_id')->on('event_log')->onDelete('cascade'); $table->index(['log_id'], 'event_log_settings_log_id'); + $table->string('locale', 14)->default(''); $table->string('setting_name', 255); $table->mediumText('setting_value')->nullable(); - $table->string('setting_type', 6)->comment('(bool|int|float|string|object)'); - $table->unique(['log_id', 'setting_name'], 'event_log_settings_unique'); + $table->unique(['log_id', 'setting_name', 'locale'], 'event_log_settings_unique'); }); // Add partial index (DBMS-specific) diff --git a/classes/migration/upgrade/v3_4_0/I8933_EventLogLocalized.php b/classes/migration/upgrade/v3_4_0/I8933_EventLogLocalized.php new file mode 100644 index 00000000000..291faf8535c --- /dev/null +++ b/classes/migration/upgrade/v3_4_0/I8933_EventLogLocalized.php @@ -0,0 +1,253 @@ +string('locale', 14)->default('')->after('log_id'); + $table->dropUnique('event_log_settings_unique'); + $table->unique(['log_id', 'locale', 'setting_name'], 'event_log_settings_unique'); + $table->dropColumn('setting_type'); + }); + + // Events can be triggered without a user, e.g., in schedule tasks + Schema::table('event_log', function (Blueprint $table) { + $table->dropForeign('event_log_user_id_foreign'); + $table->dropIndex('event_log_user_id'); + $table->bigInteger('user_id')->nullable()->change(); + $table->foreign('user_id')->references('user_id')->on('users')->onDelete('cascade'); + $table->index(['user_id'], 'event_log_user_id'); + $table->boolean('is_translated')->change(); + }); + + $this->fixConflictingSubmissionLogConstants(); + + // Rename ambiguous settings + $this->renameSettings(); + + // Localize existing submission file name entries + $sitePrimaryLocale = DB::table('site')->value('primary_locale'); + $contexts = DB::table($this->getContextTable())->get([$this->getContextIdColumn(), 'primary_locale']); + $contextIdPrimaryLocaleMap = []; + foreach ($contexts as $context) { + $contextIdPrimaryLocaleMap[$context->{$this->getContextIdColumn()}] = $context->primary_locale; + } + + DB::table('event_log_settings AS es') + ->join('event_log AS e', 'es.log_id', '=', 'e.log_id') + ->where('es.setting_name', 'filename') + ->whereIn('e.event_type', [ + 0x50000001, // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_UPLOAD, + 0x50000010, // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT, + 0x50000008, // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD, + ]) + ->orderBy('event_log_setting_id') + ->chunk(self::CHUNK_SIZE, function (Collection $logChunks) use ($sitePrimaryLocale, $contextIdPrimaryLocaleMap) { + $mapLocaleWithSettingIds = []; + foreach ($logChunks as $row) { + // Get locale based on a submission file ID log entry + $locale = $this->getContextPrimaryLocale($row, $sitePrimaryLocale, $contextIdPrimaryLocaleMap); + if (!$locale) { + continue; + } + $mapLocaleWithSettingIds[$locale] ??= []; + $mapLocaleWithSettingIds[$locale][] = $row->event_log_setting_id; + } + + // Update by chunks for each locale + foreach ($mapLocaleWithSettingIds as $locale => $settingIds) { + DB::table('event_log_settings') + ->whereIn('event_log_setting_id', $settingIds) + ->update(['locale' => $locale]); + } + }); + } + + /** + * FIX event types with identical values + * 0x40000020: SUBMISSION_LOG_DECISION_EMAIL_SENT => 0x30000007, SUBMISSION_LOG_REVIEW_REMIND => 0x40000020, SUBMISSION_LOG_REVIEW_REMIND_AUTO => 0x40000021 + */ + protected function fixConflictingSubmissionLogConstants(): void + { + DB::table('event_log')->where('event_type', 0x40000020)->lazyById(1000, 'log_id')->each(function (object $row) { + if ( + DB::table('event_log_settings') + ->where('log_id', $row->log_id) + ->whereIn('setting_name', ['recipientCount', 'subject']) + ->count() === 2 + ) { + DB::table('event_log') + ->where('log_id', $row->log_id) + ->update(['event_type' => 0x30000007]); // SUBMISSION_LOG_DECISION_EMAIL_SENT + } else if ( + !DB::table('event_log_settings') + ->where('log_id', $row->log_id) + ->whereIn('setting_name', ['senderId', 'senderName']) + ->exists() + ) { + DB::table('event_log') + ->where('log_id', $row->log_id) + ->update(['event_type' => 0x40000021]); // SUBMISSION_LOG_REVIEW_REMIND_AUTO + } + }); + } + + /** + * Retrieve the primary locale of the context associated with a given submission file + */ + protected function getContextPrimaryLocale(object $row, string $sitePrimaryLocale, array $contextIdPrimaryLocaleMap): ?string + { + // Try to determine submission/submission file ID based on the assoc type + if ($row->assoc_type === 0x0000203) { // ASSOC_TYPE_SUBMISSION_FILE + $submissionFileId = $row->assoc_id; + } else if ($row->assoc_type === 0x0100009) { // ASSOC_TYPE_SUBMISSION + $submissionId = $row->assoc_id; + } else { + throw new \Exception('Unsupported assoc_type in the event log: ' . $row->assoc_type); + } + + // Get submission from the file ID + if (!isset($submissionId)) { + $submissionId = DB::table('submission_files') + ->where('submission_file_id', $submissionFileId) + ->value('submission_id'); + } + + // Assuming submission file was removed + if (!$submissionId) { + return $sitePrimaryLocale; + } + + $contextId = DB::table('submissions')->where('submission_id', $submissionId)->value('context_id'); + + if (!$contextId) { + return $sitePrimaryLocale; + } + + return $contextIdPrimaryLocaleMap[$contextId]; + } + + /** + * Rename setting name to avoid ambiguity in the event log schema + */ + protected function renameSettings() + { + // First remove 'originalFileName' setting where 'name' setting exists + $idsToDelete = DB::table('event_log_settings AS es') + ->join('event_log AS e', 'es.log_id', '=', 'e.log_id') + ->whereIn('e.event_type', [ + 0x50000008, // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD, + 0x50000010, // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT + ]) + ->where('es.setting_name', 'name') + ->pluck('e.log_id'); + + foreach ($idsToDelete->chunk(self::CHUNK_SIZE) as $ids) { + DB::table('event_log_settings') + ->whereIn('log_id', $ids->toArray()) + ->where('setting_name', 'originalFileName') + ->delete(); + } + + // Perform setting renaming + foreach ($this->mapSettings() as $eventType => $settings) { + $idsToUpdate = DB::table('event_log') + ->where('event_type', $eventType) + ->pluck('log_id'); + + foreach ($settings as $oldSettingName => $newSettingName) { + foreach ($idsToUpdate->chunk(self::CHUNK_SIZE) as $ids) { + DB::table('event_log_settings') + ->whereIn('log_id', $ids->toArray()) + ->where('setting_name', $oldSettingName) + ->update(['setting_name' => $newSettingName]); + } + } + } + } + + /** + * Map of new setting names for the event log + * event type => [ + * old setting => new setting + * ] + */ + protected function mapSettings(): Collection + { + return collect([ + 0x10000009 => [ // PKPSubmissionEventLogEntry::SUBMISSION_LOG_COPYRIGHT_AGREED + 'name' => 'userFullName' + ], + 0x40000019 => [ // PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_CONFIRMED + 'userName' => 'editorName' + ], + 0x40000011 => [ // PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_SET_DUE_DATE + 'dueDate' => 'reviewDueDate' + ], + 0x50000001 => [ // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_UPLOAD + 'originalFileName' => 'filename' + ], + /** + * 'originalFileName' and 'name' are duplicate entries in some events, the former arises from the times before + * submission files refactoring, where it had pointed to the name of the original name of the uploaded file + * rather than the user defined localized name. + * Keep the 'name' where it exists, otherwise preserve 'originalFileName' + */ + 0x50000008 => [ // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD + 'name' => 'filename', + 'originalFileName' => 'filename' + ], + 0x50000010 => [ // SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT + 'name' => 'filename', + 'originalFileName' => 'filename' + ], + 0x10000003 => [ // PKPSubmissionEventLogEntry::SUBMISSION_LOG_ADD_PARTICIPANT + 'name' => 'userFullName' + ], + 0x10000004 => [ // PKPSubmissionEventLogEntry::SUBMISSION_LOG_REMOVE_PARTICIPANT + 'name' => 'userFullName' + ] + ]); + } + + /** + * Reverse the migration. + */ + public function down(): void + { + throw new DowngradeNotSupportedException(); + } +} \ No newline at end of file diff --git a/classes/observers/listeners/LogSubmissionSubmitted.php b/classes/observers/listeners/LogSubmissionSubmitted.php index 817567e6242..91530041ca5 100644 --- a/classes/observers/listeners/LogSubmissionSubmitted.php +++ b/classes/observers/listeners/LogSubmissionSubmitted.php @@ -16,10 +16,13 @@ namespace PKP\observers\listeners; use APP\core\Application; -use APP\log\SubmissionEventLogEntry; +use APP\facades\Repo; use Illuminate\Events\Dispatcher; -use PKP\log\SubmissionLog; +use PKP\core\Core; +use PKP\core\PKPApplication; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\observers\events\SubmissionSubmitted; +use PKP\security\Validation; class LogSubmissionSubmitted { @@ -33,11 +36,15 @@ public function subscribe(Dispatcher $events): void public function handle(SubmissionSubmitted $event) { - SubmissionLog::logEvent( - Application::get()->getRequest(), - $event->submission, - SubmissionEventLogEntry::SUBMISSION_LOG_SUBMISSION_SUBMIT, - 'submission.event.submissionSubmitted' - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $event->submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_SUBMISSION_SUBMIT, + 'userId' => Validation::loggedInAs() ?? Application::get()->getRequest()->getUser()?->getId(), + 'message' => 'submission.event.submissionSubmitted', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + ]); + Repo::eventLog()->add($eventLog); } } diff --git a/classes/publication/Repository.php b/classes/publication/Repository.php index 82c11489405..f9de387e0df 100644 --- a/classes/publication/Repository.php +++ b/classes/publication/Repository.php @@ -18,7 +18,6 @@ use APP\core\Services; use APP\facades\Repo; use APP\file\PublicFileManager; -use APP\log\SubmissionEventLogEntry; use APP\publication\DAO; use APP\publication\Publication; use APP\submission\Submission; @@ -26,13 +25,14 @@ use Illuminate\Support\LazyCollection; use PKP\context\Context; use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; use PKP\file\TemporaryFileManager; -use PKP\log\PKPSubmissionEventLogEntry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\observers\events\PublicationPublished; use PKP\observers\events\PublicationUnpublished; use PKP\plugins\Hook; +use PKP\security\Validation; use PKP\services\PKPSchemaService; use PKP\submission\Genre; use PKP\submission\PKPSubmission; @@ -316,7 +316,8 @@ public function version(Publication $publication): int $newPublication->setData('version', $publication->getData('version') + 1); $newPublication->stampModified(); - $context = Application::get()->getRequest()->getContext(); + $request = Application::get()->getRequest(); + $context = $request->getContext(); if ($context->getData(Context::SETTING_DOI_VERSIONING)) { $newPublication->setData('doiId', null); @@ -348,7 +349,17 @@ public function version(Publication $publication): int Hook::call('Publication::version', [&$newPublication, $publication]); $submission = Repo::submission()->get($newPublication->getData('submissionId')); - SubmissionLog::logEvent($this->request, $submission, PKPSubmissionEventLogEntry::SUBMISSION_LOG_CREATE_VERSION, 'publication.event.versionCreated'); + + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_CREATE_VERSION, + 'userId' => Validation::loggedInAs() ?? $request->getUser()?->getId(), + 'message' => 'publication.event.versionCreated', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + ]); + Repo::eventLog()->add($eventLog); return $newPublication->getId(); } @@ -357,11 +368,10 @@ public function version(Publication $publication): int public function edit(Publication $publication, array $params): Publication { $submission = Repo::submission()->get($publication->getData('submissionId')); + $userId = $this->request->getUser()?->getId(); // Move uploaded files into place and update the params if (array_key_exists('coverImage', $params)) { - $userId = $this->request->getUser() ? $this->request->getUser()->getId() : null; - $submissionContext = $this->request->getContext(); if ($submissionContext->getId() !== $submission->getData('contextId')) { $submissionContext = Services::get('context')->get($submission->getData('contextId')); @@ -388,7 +398,16 @@ public function edit(Publication $publication, array $params): Publication $submission = Repo::submission()->get($newPublication->getData('submissionId')); // Log an event when publication data is updated - SubmissionLog::logEvent($this->request, $submission, PKPSubmissionEventLogEntry::SUBMISSION_LOG_METADATA_UPDATE, 'submission.event.general.metadataUpdated'); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_METADATA_UPDATE, + 'userId' => Validation::loggedInAs() ?? $userId, + 'message' => 'submission.event.general.metadataUpdated', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + ]); + Repo::eventLog()->add($eventLog); return $newPublication; } @@ -469,12 +488,16 @@ public function publish(Publication $publication) $msg = ($newPublication->getData('status') === Submission::STATUS_SCHEDULED) ? 'publication.event.versionScheduled' : 'publication.event.versionPublished'; } - SubmissionLog::logEvent( - $this->request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_METADATA_PUBLISH, - $msg - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_METADATA_PUBLISH, + 'userId' => Validation::loggedInAs() ?? $this->request->getUser()?->getId(), + 'message' => $msg, + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); // Mark DOIs stale (if applicable). if ($newPublication->getData('status') === Submission::STATUS_PUBLISHED) { @@ -556,12 +579,16 @@ public function unpublish(Publication $publication) Repo::doi()->markStale($staleDoiIds); } - SubmissionLog::logEvent( - $this->request, - $submission, - PKPSubmissionEventLogEntry::SUBMISSION_LOG_METADATA_UNPUBLISH, - $msg - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_METADATA_UNPUBLISH, + 'userId' => Validation::loggedInAs() ?? $this->request->getUser()?->getId(), + 'message' => $msg, + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); Hook::call( 'Publication::unpublish', diff --git a/classes/security/Validation.php b/classes/security/Validation.php index 7176376e6f0..6bfdfe9ebe7 100644 --- a/classes/security/Validation.php +++ b/classes/security/Validation.php @@ -405,19 +405,30 @@ public static function isLoggedIn() } /** - * Check if the user is logged in as a different user. - * - * @return bool + * Check if the user is logged in as a different user. Returns the original user ID or null */ - public static function isLoggedInAs() + public static function loggedInAs(): ?int { if (!SessionManager::hasSession()) { return false; } $sessionManager = SessionManager::getManager(); $session = $sessionManager->getUserSession(); - $signedInAs = $session->getSessionVar('signedInAs'); - return !!$signedInAs; + $userId = $session->getSessionVar('signedInAs'); + + return $userId ? (int) $userId : null; + } + + /** + * Check if the user is logged in as a different user. + * + * @return bool + * + * @deprecated 3.4 + */ + public static function isLoggedInAs(): bool + { + return (bool) static::loggedInAs(); } /** diff --git a/classes/services/PKPNavigationMenuService.php b/classes/services/PKPNavigationMenuService.php index 18749cb9b92..276b839a83d 100755 --- a/classes/services/PKPNavigationMenuService.php +++ b/classes/services/PKPNavigationMenuService.php @@ -152,7 +152,7 @@ public function getDisplayStatus(&$navigationMenuItem, &$navigationMenu) $templateMgr = TemplateManager::getManager($request); $isUserLoggedIn = Validation::isLoggedIn(); - $isUserLoggedInAs = Validation::isLoggedInAs(); + $isUserLoggedInAs = Validation::loggedInAs(); $context = $request->getContext(); $currentUser = $request->getUser(); diff --git a/classes/services/PKPSchemaService.php b/classes/services/PKPSchemaService.php index d6bb0475142..a5808e349bc 100644 --- a/classes/services/PKPSchemaService.php +++ b/classes/services/PKPSchemaService.php @@ -46,6 +46,7 @@ class PKPSchemaService public const SCHEMA_SUBMISSION_FILE = 'submissionFile'; public const SCHEMA_USER = 'user'; public const SCHEMA_USER_GROUP = 'userGroup'; + public const SCHEMA_EVENT_LOG = 'eventLog'; /** @var array cache of schemas that have been loaded */ private $_schemas = []; diff --git a/classes/submission/DAO.php b/classes/submission/DAO.php index 0a9a2ee6188..0bc932f656a 100644 --- a/classes/submission/DAO.php +++ b/classes/submission/DAO.php @@ -24,8 +24,8 @@ use PKP\core\EntityDAO; use PKP\core\traits\EntityWithParent; use PKP\db\DAORegistry; +use PKP\log\event\EventLogEntry; use PKP\log\SubmissionEmailLogDAO; -use PKP\log\SubmissionEventLogDAO; use PKP\note\NoteDAO; use PKP\notification\NotificationDAO; use PKP\query\QueryDAO; @@ -297,8 +297,12 @@ public function deleteById(int $id) $notificationDao = DAORegistry::getDAO('NotificationDAO'); /** @var NotificationDAO $notificationDao */ $notificationDao->deleteByAssoc(Application::ASSOC_TYPE_SUBMISSION, $id); - $submissionEventLogDao = DAORegistry::getDAO('SubmissionEventLogDAO'); /** @var SubmissionEventLogDAO $submissionEventLogDao */ - $submissionEventLogDao->deleteByAssoc(Application::ASSOC_TYPE_SUBMISSION, $id); + Repo::eventLog()->getCollector() + ->filterByAssoc(Application::ASSOC_TYPE_SUBMISSION, [$id]) + ->getMany() + ->each(function (EventLogEntry $logEntry) { + Repo::eventLog()->delete($logEntry); + }); $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /** @var SubmissionEmailLogDAO $submissionEmailLogDao */ $submissionEmailLogDao->deleteByAssoc(Application::ASSOC_TYPE_SUBMISSION, $id); diff --git a/classes/submission/action/EditorAction.php b/classes/submission/action/EditorAction.php index 6fad3d5ba77..12e9de2a5ad 100644 --- a/classes/submission/action/EditorAction.php +++ b/classes/submission/action/EditorAction.php @@ -28,8 +28,7 @@ use PKP\core\PKPServices; use PKP\core\PKPString; use PKP\db\DAORegistry; -use PKP\log\PKPSubmissionEventLogEntry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\mailables\ReviewRequest; use PKP\mail\mailables\ReviewRequestSubsequent; use PKP\mail\variables\ReviewAssignmentEmailVariable; @@ -37,6 +36,7 @@ use PKP\notification\PKPNotificationManager; use PKP\plugins\Hook; use PKP\security\AccessKeyManager; +use PKP\security\Validation; use PKP\submission\PKPSubmission; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; @@ -110,12 +110,26 @@ public function addReviewer($request, $submission, $reviewerId, &$reviewRound, $ ); // Add log - SubmissionLog::logEvent($request, $submission, PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_ASSIGN, 'log.review.reviewerAssigned', ['reviewAssignmentId' => $reviewAssignment->getId(), 'reviewerName' => $reviewer->getFullName(), 'submissionId' => $submission->getId(), 'stageId' => $stageId, 'round' => $round]); + $user = $request->getUser(); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_ASSIGN, + 'userId' => Validation::loggedInAs() ?? $user->getId(), + 'message' => 'log.review.reviewerAssigned', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignment' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'submissionId' => $submission->getId(), + 'stageId' => $stageId, + 'round' => $round + ]); + Repo::eventLog()->add($eventLog); // Send mail if (!$request->getUserVar('skipEmail')) { $context = PKPServices::get('context')->get($submission->getData('contextId')); - $user = $request->getUser(); $emailTemplate = Repo::emailTemplate()->getByKey($submission->getData('contextId'), $request->getUserVar('template')); $emailBody = $request->getUserVar('personalMessage'); $emailSubject = $emailTemplate->getLocalizedData('subject'); @@ -126,7 +140,7 @@ public function addReviewer($request, $submission, $reviewerId, &$reviewRound, $ } catch (TransportException $e) { $notificationMgr = new PKPNotificationManager(); $notificationMgr->createTrivialNotification( - $request->getUser()->getId(), + $user->getId(), PKPNotification::NOTIFICATION_TYPE_ERROR, ['contents' => __('email.compose.error')] ); @@ -172,23 +186,25 @@ public function setDueDates($request, $submission, $reviewAssignment, $reviewDue // N.B. Only logging Date Due if ($logEntry) { // Add log - SubmissionLog::logEvent( - $request, - $submission, - PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_SET_DUE_DATE, - 'log.review.reviewDueDateSet', - [ - 'reviewAssignmentId' => $reviewAssignment->getId(), - 'reviewerName' => $reviewer->getFullName(), - 'dueDate' => date( - PKPString::convertStrftimeFormat($context->getLocalizedDateFormatShort()), - strtotime($reviewAssignment->getDateDue()) - ), - 'submissionId' => $submission->getId(), - 'stageId' => $reviewAssignment->getStageId(), - 'round' => $reviewAssignment->getRound() - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_SET_DUE_DATE, + 'userId' => Validation::loggedInAs() ?? $request->getUser()->getId(), + 'message' => 'log.review.reviewDueDateSet', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignmentId' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'reviewDueDate' => date( + PKPString::convertStrftimeFormat($context->getLocalizedDateFormatShort()), + strtotime($reviewAssignment->getDateDue()) + ), + 'submissionId' => $submission->getId(), + 'stageId' => $reviewAssignment->getStageId(), + 'round' => $reviewAssignment->getRound() + ]); + Repo::eventLog()->add($eventLog); } } } diff --git a/classes/submission/reviewer/ReviewerAction.php b/classes/submission/reviewer/ReviewerAction.php index 34496372909..9c31e640de8 100644 --- a/classes/submission/reviewer/ReviewerAction.php +++ b/classes/submission/reviewer/ReviewerAction.php @@ -18,22 +18,23 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; +use APP\log\event\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use Illuminate\Support\Facades\Mail; use PKP\context\Context; use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\core\PKPRequest; use PKP\db\DAORegistry; use PKP\log\SubmissionEmailLogDAO; use PKP\log\SubmissionEmailLogEntry; -use PKP\log\SubmissionLog; use PKP\mail\mailables\ReviewConfirm; use PKP\mail\mailables\ReviewDecline; use PKP\notification\PKPNotification; use PKP\plugins\Hook; use PKP\security\Role; +use PKP\security\Validation; use PKP\stageAssignment\StageAssignmentDAO; use PKP\submission\PKPSubmission; use PKP\submission\reviewAssignment\ReviewAssignment; @@ -96,18 +97,20 @@ public function confirmReview( $reviewAssignmentDao->updateObject($reviewAssignment); // Add log - SubmissionLog::logEvent( - $request, - $submission, - $decline ? SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_DECLINE : SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_ACCEPT, - $decline ? 'log.review.reviewDeclined' : 'log.review.reviewAccepted', - [ - 'reviewAssignmentId' => $reviewAssignment->getId(), - 'reviewerName' => $reviewer->getFullName(), - 'submissionId' => $reviewAssignment->getSubmissionId(), - 'round' => $reviewAssignment->getRound() - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => $decline ? SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_DECLINE : SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_ACCEPT, + 'userId' => Validation::loggedInAs() ?? $request->getUser()->getId(), + 'message' => $decline ? 'log.review.reviewDeclined' : 'log.review.reviewAccepted', + 'isTranslate' => 0, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignmentId' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'submissionId' => $reviewAssignment->getSubmissionId(), + 'round' => $reviewAssignment->getRound() + ]); + Repo::eventLog()->add($eventLog); } } diff --git a/classes/submission/reviewer/form/PKPReviewerReviewStep3Form.php b/classes/submission/reviewer/form/PKPReviewerReviewStep3Form.php index 7c0e158fa17..0394f1151d5 100644 --- a/classes/submission/reviewer/form/PKPReviewerReviewStep3Form.php +++ b/classes/submission/reviewer/form/PKPReviewerReviewStep3Form.php @@ -18,7 +18,6 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use APP\template\TemplateManager; @@ -28,7 +27,7 @@ use PKP\core\PKPApplication; use PKP\core\PKPRequest; use PKP\db\DAORegistry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\mailables\ReviewCompleteNotifyEditors; use PKP\notification\NotificationDAO; use PKP\notification\NotificationSubscriptionSettingsDAO; @@ -39,6 +38,7 @@ use PKP\reviewForm\ReviewFormResponse; use PKP\reviewForm\ReviewFormResponseDAO; use PKP\security\Role; +use PKP\security\Validation; use PKP\stageAssignment\StageAssignmentDAO; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; @@ -240,19 +240,20 @@ public function execute(...$functionParams) // Add log $reviewer = Repo::user()->get($reviewAssignment->getReviewerId(), true); - $request = Application::get()->getRequest(); - SubmissionLog::logEvent( - $request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_READY, - 'log.review.reviewReady', - [ - 'reviewAssignmentId' => $reviewAssignment->getId(), - 'reviewerName' => $reviewer->getFullName(), - 'submissionId' => $reviewAssignment->getSubmissionId(), - 'round' => $reviewAssignment->getRound() - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_READY, + 'userId' => Validation::loggedInAs() ?? Application::get()->getRequest()->getUser()->getId(), + 'message' => 'log.review.reviewReady', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignmentId' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'submissionId' => $reviewAssignment->getSubmissionId(), + 'round' => $reviewAssignment->getRound() + ]); + Repo::eventLog()->add($eventLog); parent::execute(...$functionParams); } diff --git a/classes/submissionFile/Repository.php b/classes/submissionFile/Repository.php index dd0de0fe2f3..f276991c1b6 100644 --- a/classes/submissionFile/Repository.php +++ b/classes/submissionFile/Repository.php @@ -28,9 +28,7 @@ use PKP\db\DAORegistry; use PKP\log\SubmissionEmailLogDAO; use PKP\log\SubmissionEmailLogEntry; -use PKP\log\SubmissionFileEventLogEntry; -use PKP\log\SubmissionFileLog; -use PKP\log\SubmissionLog; +use PKP\log\event\SubmissionFileEventLogEntry; use PKP\mail\mailables\RevisedVersionNotify; use PKP\note\NoteDAO; use PKP\notification\PKPNotification; @@ -38,6 +36,7 @@ use PKP\query\QueryDAO; use PKP\security\authorization\SubmissionFileAccessPolicy; use PKP\security\Role; +use PKP\security\Validation; use PKP\services\PKPSchemaService; use PKP\stageAssignment\StageAssignmentDAO; use PKP\submission\reviewRound\ReviewRoundDAO; @@ -123,7 +122,7 @@ public function validate( [] ); - // Check required fields if we're adding a context + // Check required fields ValidatorFactory::required( $validator, $object, @@ -267,39 +266,35 @@ public function add(SubmissionFile $submissionFile): int Hook::call('SubmissionFile::add', [$submissionFile]); - $user = $this->request->getUser(); - SubmissionFileLog::logEvent( - $this->request, - $submissionFile, - SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_UPLOAD, - 'submission.event.fileUploaded', + $logData = $this->getSubmissionFileLogData($submissionFile); + + $logEntry = Repo::eventLog()->newDataObject(array_merge( + $logData, [ - 'fileStage' => $submissionFile->getData('fileStage'), - 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), - 'submissionFileId' => $submissionFile->getId(), - 'fileId' => $submissionFile->getData('fileId'), - 'submissionId' => $submissionFile->getData('submissionId'), - 'originalFileName' => $submissionFile->getLocalizedData('name'), - 'username' => $user ? $user->getUsername() : null, + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, + 'assocId' => $submissionFile->getId(), + 'eventType' => SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_UPLOAD, + 'dateLogged' => Core::getCurrentDate(), + 'message' => 'submission.event.fileUploaded', + 'isTranslated' => false, ] - ); + )); + Repo::eventLog()->add($logEntry); $submission = Repo::submission()->get($submissionFile->getData('submissionId')); - SubmissionLog::logEvent( - $this->request, - $submission, - SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD, - 'submission.event.fileRevised', + $logEntry = Repo::eventLog()->newDataObject(array_merge( + $logData, [ - 'fileStage' => $submissionFile->getData('fileStage'), - 'submissionFileId' => $submissionFile->getId(), - 'fileId' => $submissionFile->getData('fileId'), - 'submissionId' => $submissionFile->getData('submissionId'), - 'username' => $user ? $user->getUsername() : null, - 'name' => $submissionFile->getLocalizedData('name'), + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD, + 'dateLogged' => Core::getCurrentDate(), + 'message' => 'submission.event.fileRevised', + 'isTranslated' => false, ] - ); + )); + Repo::eventLog()->add($logEntry); // Update status and notifications when revisions have been uploaded if ($submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION || @@ -366,40 +361,33 @@ public function edit( $newFileUploaded = !empty($params['fileId']) && $params['fileId'] !== $submissionFile->getData('fileId'); - $user = $this->request->getUser(); - SubmissionFileLog::logEvent( - $this->request, - $submissionFile, - $newFileUploaded ? SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD : SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT, - $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited', + $logData = $this->getSubmissionFileLogData($submissionFile); + $logEntry = Repo::eventLog()->newDataObject(array_merge( + $logData, [ - 'fileStage' => $submissionFile->getData('fileStage'), - 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), - 'submissionFileId' => $submissionFile->getId(), - 'fileId' => $submissionFile->getData('fileId'), - 'submissionId' => $submissionFile->getData('submissionId'), - 'originalFileName' => $submissionFile->getLocalizedData('name'), - 'username' => $user ? $user->getUsername() : null, + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, + 'assocId' => $submissionFile->getId(), + 'eventType' => $newFileUploaded ? SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD : SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT, + 'message' => $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), ] - ); + )); + Repo::eventLog()->add($logEntry); $submission = Repo::submission()->get($submissionFile->getData('submissionId')); - SubmissionLog::logEvent( - $this->request, - $submission, - $newFileUploaded ? SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD : SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT, - $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited', + + Repo::eventLog()->newDataObject(array_merge( + $logData, [ - 'fileStage' => $submissionFile->getData('fileStage'), - 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), - 'submissionFileId' => $submissionFile->getId(), - 'fileId' => $submissionFile->getData('fileId'), - 'submissionId' => $submissionFile->getData('submissionId'), - 'username' => $user ? $user->getUsername() : null, - 'originalFileName' => $submissionFile->getLocalizedData('name'), - 'name' => $submissionFile->getLocalizedData('name'), + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => $newFileUploaded ? SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD : SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT, + 'message' => $newFileUploaded ? 'submission.event.revisionUploaded' : 'submission.event.fileEdited', + 'isTranslate' => 0, + 'dateLogged' => Core::getCurrentDate(), ] - ); + )); } /** @@ -509,20 +497,18 @@ public function delete(SubmissionFile $submissionFile): void } // Log the deletion - $user = Application::get()->getRequest()->getUser(); - SubmissionFileLog::logEvent( - Application::get()->getRequest(), - $submissionFile, - SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_DELETE, - 'submission.event.fileDeleted', + $logEntry = Repo::eventLog()->newDataObject(array_merge( + $this->getSubmissionFileLogData($submissionFile), [ - 'fileStage' => $submissionFile->getData('fileStage'), - 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), - 'submissionFileId' => $submissionFile->getId(), - 'submissionId' => $submissionFile->getData('submissionId'), - 'username' => $user ? $user->getUsername() : null, + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, + 'assocId' => $submissionFile->getId(), + 'eventType' => SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_DELETE, + 'message' => 'submission.event.fileDeleted', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), ] - ); + )); + Repo::eventLog()->add($logEntry); Hook::call('SubmissionFile::delete', [$submissionFile]); } @@ -839,4 +825,23 @@ protected function notifyEditorsRevisionsUploaded(SubmissionFile $submissionFile $user ); } + + /** + * Derive data from the submission file to record in the event log + */ + protected function getSubmissionFileLogData(SubmissionFile $submissionFile): array + { + $user = $this->request->getUser(); + + return [ + 'userId' => Validation::loggedInAs() ?? $user?->getId(), + 'fileStage' => $submissionFile->getData('fileStage'), + 'submissionFileId' => $submissionFile->getId(), + 'sourceSubmissionFileId' => $submissionFile->getData('sourceSubmissionFileId'), + 'fileId' => $submissionFile->getData('fileId'), + 'submissionId' => $submissionFile->getData('submissionId'), + 'filename' => $submissionFile->getData('name'), + 'username' => $user?->getUsername(), + ]; + } } diff --git a/classes/task/ReviewReminder.php b/classes/task/ReviewReminder.php index d03975f97cd..ed21cda13f6 100644 --- a/classes/task/ReviewReminder.php +++ b/classes/task/ReviewReminder.php @@ -18,13 +18,12 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use Illuminate\Support\Facades\Mail; use PKP\context\Context; use PKP\core\Core; use PKP\core\PKPApplication; use PKP\db\DAORegistry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\mailables\ReviewRemindAuto; use PKP\mail\mailables\ReviewResponseRemindAuto; use PKP\scheduledTask\ScheduledTask; @@ -97,16 +96,18 @@ public function sendReminder( $reviewAssignment->setReminderWasAutomatic(1); $reviewAssignmentDao->updateObject($reviewAssignment); - SubmissionLog::logEvent( - $request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_REMIND_AUTO, - 'submission.event.reviewer.reviewerRemindedAuto', - [ - 'recipientId' => $reviewer->getId(), - 'recipientName' => $reviewer->getFullName(), - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_REMIND_AUTO, + 'userId' => null, + 'message' => 'submission.event.reviewer.reviewerRemindedAuto', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'recipientId' => $reviewer->getId(), + 'recipientName' => $reviewer->getFullName(), + ]); + Repo::eventLog()->add($eventLog); } /** diff --git a/classes/template/PKPTemplateManager.php b/classes/template/PKPTemplateManager.php index a640fe03518..f7643704c15 100644 --- a/classes/template/PKPTemplateManager.php +++ b/classes/template/PKPTemplateManager.php @@ -383,7 +383,7 @@ public function initialize($request) if (!SessionManager::isDisabled()) { $this->assign([ 'isUserLoggedIn' => Validation::isLoggedIn(), - 'isUserLoggedInAs' => Validation::isLoggedInAs(), + 'isUserLoggedInAs' => (bool) Validation::loggedInAs(), 'itemsPerPage' => Config::getVar('interface', 'items_per_page'), 'numPageLinks' => Config::getVar('interface', 'page_links'), 'siteTitle' => $request->getSite()->getLocalizedData('title'), diff --git a/classes/user/Repository.php b/classes/user/Repository.php index 108e007a484..6fb51d61297 100644 --- a/classes/user/Repository.php +++ b/classes/user/Repository.php @@ -320,8 +320,7 @@ public function mergeUsers(int $oldUserId, int $newUserId) $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /** @var SubmissionEmailLogDAO $submissionEmailLogDao */ $submissionEmailLogDao->changeUser($oldUserId, $newUserId); - $submissionEventLogDao = DAORegistry::getDAO('SubmissionEventLogDAO'); /** @var SubmissionEventLogDAO $submissionEventLogDao */ - $submissionEventLogDao->changeUser($oldUserId, $newUserId); + Repo::eventLog()->dao->changeUser($oldUserId, $newUserId); $submissionCommentDao = DAORegistry::getDAO('SubmissionCommentDAO'); /** @var SubmissionCommentDAO $submissionCommentDao */ $submissionComments = $submissionCommentDao->getByUserId($oldUserId); diff --git a/controllers/api/file/PKPManageFileApiHandler.php b/controllers/api/file/PKPManageFileApiHandler.php index b8fc2c63efe..5e72c85248a 100644 --- a/controllers/api/file/PKPManageFileApiHandler.php +++ b/controllers/api/file/PKPManageFileApiHandler.php @@ -18,13 +18,16 @@ use APP\core\Application; use APP\core\Request; +use APP\core\Services; use APP\facades\Repo; use APP\handler\Handler; use APP\notification\NotificationManager; use APP\template\TemplateManager; use PKP\controllers\wizard\fileUpload\form\SubmissionFilesMetadataForm; use PKP\core\JSONMessage; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; +use PKP\log\event\EventLogEntry; use PKP\notification\NotificationDAO; use PKP\notification\PKPNotification; use PKP\observers\events\MetadataChanged; @@ -43,7 +46,7 @@ public function __construct() parent::__construct(); $this->addRoleAssignment( [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR, Role::ROLE_ID_ASSISTANT, Role::ROLE_ID_REVIEWER, Role::ROLE_ID_AUTHOR], - ['deleteFile', 'editMetadata', 'editMetadataTab', 'saveMetadata'] + ['deleteFile', 'editMetadata', 'editMetadataTab', 'saveMetadata', 'cancelFileUpload'] ); } @@ -91,6 +94,71 @@ public function deleteFile($args, $request) return \PKP\db\DAO::getDataChangedEvent(); } + /** + * Restore original file when cancelling the upload wizard + */ + public function cancelFileUpload(array $args, Request $request): JSONMessage + { + if (!$request->checkCSRF()) { + return new JSONMessage(false); + } + + $submissionFile = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION_FILE); + $originalFile = $request->getUserVar('originalFile') ? (array)$request->getUserVar('originalFile') : null; + $revisedFileId = $request->getUserVar('fileId') ? (int)$request->getUserVar('fileId') : null; + + // Get revisions and check file IDs + $revisions = Repo::submissionFile()->getRevisions($submissionFile->getId()); + $revisionIds = []; + foreach ($revisions as $revision) { + $revisionIds[] = $revision->fileId; + } + + if (!$revisedFileId || !in_array($revisedFileId, $revisionIds)) { + return new JSONMessage(false); + } + + if (!isset($originalFile['fileId']) || !in_array($originalFile['fileId'], $revisionIds)) { + return new JSONMessage(false); + } + + $originalFileId = (int) $originalFile['fileId']; + + // Get the file name and uploader user ID + $originalUserId = $originalFile['uploaderUserId'] ? (int)$originalFile['uploaderUserId'] : null; + $originalFileName = $originalFile['name'] ? (array)$originalFile['name'] : null; + if (!$originalUserId || !$originalFileName) { + return new JSONMessage(false); + } + + $originalUser = Repo::user()->get($originalUserId); + if (!$originalUser) { + return new JSONMessage(false); + } + + $originalUsername = $originalUser->getUsername(); + $matchedLogEntry = $this->findMatchedLogEntry($submissionFile, $originalFileId, $originalUsername, $originalFileName); + if (!$matchedLogEntry) { + return new JSONMessage(false); + } + + // Restore original submission file + Repo::submissionFile()->edit( + $submissionFile, + [ + 'fileId' => $matchedLogEntry->getData('fileId'), + 'name' => $matchedLogEntry->getData('filename'), + 'uploaderUserId' => Repo::user()->getByUsername($matchedLogEntry->getData('username'))->getId(), + ] + ); + + // Remove uploaded file + Services::get('file')->delete($revisedFileId); + + $this->setupTemplate($request); + return \PKP\db\DAO::getDataChangedEvent(); + } + /** * Edit submission file metadata modal. * @@ -198,4 +266,42 @@ protected function getUpdateNotifications() { return [PKPNotification::NOTIFICATION_TYPE_PENDING_EXTERNAL_REVISIONS]; } + + /** + * Compare user supplied data when cancelling file upload with saved in the event log; + * assuming we found the right entry if they match + */ + protected function findMatchedLogEntry( + SubmissionFile $submissionFile, + int $originalFileId, + string $originalUsername, + array $originalFileName + ): ?EventLogEntry + { + $logEntries = Repo::eventLog()->getCollector() + ->filterByAssoc(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, [$submissionFile->getId()]) + ->getMany(); + + $match = null; + foreach ($logEntries as $logEntry) { + + $loggedUsername = $logEntry->getData('username'); + $loggedFileName = $logEntry->getData('filename'); + $loggedFileId = $logEntry->getData('fileId'); + if (!$loggedUsername || !$loggedFileName || !$loggedFileId) { + continue; + } + + if ( + $loggedUsername === $originalUsername && + $loggedFileName == $originalFileName && + $loggedFileId === $originalFileId + ) { + $match = $logEntry; + break; + } + } + + return $match; + } } diff --git a/controllers/grid/eventLog/EventLogGridCellProvider.php b/controllers/grid/eventLog/EventLogGridCellProvider.php index 220995fd34c..387143efe35 100644 --- a/controllers/grid/eventLog/EventLogGridCellProvider.php +++ b/controllers/grid/eventLog/EventLogGridCellProvider.php @@ -21,8 +21,8 @@ use PKP\controllers\grid\DataObjectGridCellProvider; use PKP\controllers\grid\GridColumn; use PKP\db\DAORegistry; -use PKP\log\EventLogEntry; -use PKP\log\PKPSubmissionEventLogEntry; +use PKP\log\event\EventLogEntry; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use PKP\submissionFile\SubmissionFile; diff --git a/controllers/grid/eventLog/EventLogGridRow.php b/controllers/grid/eventLog/EventLogGridRow.php index 68dd9a2930e..595c33dc24c 100644 --- a/controllers/grid/eventLog/EventLogGridRow.php +++ b/controllers/grid/eventLog/EventLogGridRow.php @@ -24,8 +24,8 @@ use PKP\controllers\grid\GridRow; use PKP\db\DAORegistry; use PKP\log\EmailLogEntry; -use PKP\log\EventLogEntry; -use PKP\log\SubmissionFileEventLogEntry; +use PKP\log\event\EventLogEntry; +use PKP\log\event\SubmissionFileEventLogEntry; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use PKP\submissionFile\SubmissionFile; diff --git a/controllers/grid/eventLog/SubmissionEventLogGridHandler.php b/controllers/grid/eventLog/SubmissionEventLogGridHandler.php index 18fe77c2707..b6593d11d5f 100644 --- a/controllers/grid/eventLog/SubmissionEventLogGridHandler.php +++ b/controllers/grid/eventLog/SubmissionEventLogGridHandler.php @@ -17,6 +17,7 @@ namespace PKP\controllers\grid\eventLog; use APP\core\Application; +use APP\facades\Repo; use APP\submission\Submission; use PKP\controllers\grid\DateGridCellProvider; use PKP\controllers\grid\GridColumn; @@ -27,9 +28,8 @@ use PKP\core\PKPString; use PKP\db\DAORegistry; use PKP\log\EmailLogEntry; -use PKP\log\EventLogEntry; use PKP\log\SubmissionEmailLogDAO; -use PKP\log\SubmissionEventLogDAO; +use PKP\log\event\EventLogEntry; use PKP\security\authorization\internal\UserAccessibleWorkflowStageRequiredPolicy; use PKP\security\authorization\SubmissionAccessPolicy; use PKP\security\Role; @@ -201,12 +201,13 @@ public function getRequestArgs() */ protected function loadData($request, $filter = null) { - $submissionEventLogDao = DAORegistry::getDAO('SubmissionEventLogDAO'); /** @var SubmissionEventLogDAO $submissionEventLogDao */ $submissionEmailLogDao = DAORegistry::getDAO('SubmissionEmailLogDAO'); /** @var SubmissionEmailLogDAO $submissionEmailLogDao */ $submission = $this->getSubmission(); - $eventLogEntries = $submissionEventLogDao->getBySubmissionId($submission->getId()); + $eventLogEntries = Repo::eventLog()->getCollector() + ->filterByAssoc(PKPApplication::ASSOC_TYPE_SUBMISSION, [$submission->getId()]) + ->getMany(); $emailLogEntries = $submissionEmailLogDao->getBySubmissionId($submission->getId()); $entries = array_merge($eventLogEntries->toArray(), $emailLogEntries->toArray()); diff --git a/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.php b/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.php index c76476832a9..1914a88dac6 100644 --- a/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.php +++ b/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.php @@ -17,9 +17,9 @@ namespace PKP\controllers\grid\eventLog; use APP\core\Application; +use APP\facades\Repo; +use PKP\core\PKPApplication; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; -use PKP\log\SubmissionFileEventLogDAO; use PKP\security\authorization\SubmissionFileAccessPolicy; use PKP\security\Role; use PKP\submissionFile\SubmissionFile; @@ -114,14 +114,10 @@ public function getRequestArgs() */ protected function loadData($request, $filter = null) { - $submissionFile = $this->getSubmissionFile(); - $submissionFileEventLogDao = DAORegistry::getDAO('SubmissionFileEventLogDAO'); /** @var SubmissionFileEventLogDAO $submissionFileEventLogDao */ - $eventLogEntries = $submissionFileEventLogDao->getBySubmissionFileId( - $submissionFile->getId() - ); - $eventLogEntries = $eventLogEntries->toArray(); - - return $eventLogEntries; + return Repo::eventLog()->getCollector() + ->filterByAssoc(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, [$this->getSubmissionFile()->getId()]) + ->getMany() + ->toArray(); } /** diff --git a/controllers/grid/settings/user/UserGridRow.php b/controllers/grid/settings/user/UserGridRow.php index b40213834ec..15e6fc16c93 100644 --- a/controllers/grid/settings/user/UserGridRow.php +++ b/controllers/grid/settings/user/UserGridRow.php @@ -173,7 +173,7 @@ public function initialize($request, $template = null) $canAdminister = Validation::getAdministrationLevel($this->getId(), $request->getUser()->getId()) === Validation::ADMINISTRATION_FULL; if ( - !Validation::isLoggedInAs() && + !Validation::loggedInAs() && $request->getUser()->getId() != $this->getId() && $canAdminister ) { diff --git a/controllers/grid/users/reviewer/ReviewerGridRow.php b/controllers/grid/users/reviewer/ReviewerGridRow.php index d4850490652..8873cc3d4bf 100644 --- a/controllers/grid/users/reviewer/ReviewerGridRow.php +++ b/controllers/grid/users/reviewer/ReviewerGridRow.php @@ -179,7 +179,7 @@ public function initialize($request, $template = null) $user = $request->getUser(); if ( - !Validation::isLoggedInAs() && + !Validation::loggedInAs() && $user->getId() != $reviewAssignment->getReviewerId() && Validation::getAdministrationLevel($reviewAssignment->getReviewerId(), $user->getId()) === Validation::ADMINISTRATION_FULL && !$reviewAssignment->getCancelled() diff --git a/controllers/grid/users/reviewer/form/ReinstateReviewerForm.php b/controllers/grid/users/reviewer/form/ReinstateReviewerForm.php index 2ebcf60a03f..7e3119c6642 100644 --- a/controllers/grid/users/reviewer/form/ReinstateReviewerForm.php +++ b/controllers/grid/users/reviewer/form/ReinstateReviewerForm.php @@ -17,16 +17,18 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use PKP\context\Context; +use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\Mailable; use PKP\mail\mailables\ReviewerReinstate; use PKP\notification\PKPNotification; use PKP\plugins\Hook; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use PKP\submission\reviewRound\ReviewRound; @@ -87,7 +89,21 @@ public function execute(...$functionArgs) $notificationMgr->createTrivialNotification($currentUser->getId(), PKPNotification::NOTIFICATION_TYPE_SUCCESS, ['contents' => __('notification.reinstatedReviewer')]); // Add log - SubmissionLog::logEvent($request, $submission, SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_REINSTATED, 'log.review.reviewReinstated', ['reviewAssignmentId' => $reviewAssignment->getId(), 'reviewerName' => $reviewer->getFullName(), 'submissionId' => $submission->getId(), 'stageId' => $reviewAssignment->getStageId(), 'round' => $reviewAssignment->getRound()]); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_REINSTATED, + 'userId' => Validation::loggedInAs() ?? $currentUser->getId(), + 'message' => 'log.review.reviewReinstated', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignmentId' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'submissionId' => $submission->getId(), + 'stageId' => $reviewAssignment->getStageId(), + 'round' => $reviewAssignment->getRound() + ]); + Repo::eventLog()->add($eventLog); return true; } diff --git a/controllers/grid/users/reviewer/form/ResendRequestReviewerForm.php b/controllers/grid/users/reviewer/form/ResendRequestReviewerForm.php index 004b638e95c..df3fa703fcf 100644 --- a/controllers/grid/users/reviewer/form/ResendRequestReviewerForm.php +++ b/controllers/grid/users/reviewer/form/ResendRequestReviewerForm.php @@ -18,15 +18,17 @@ use APP\core\Application; use APP\core\Request; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use PKP\context\Context; +use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\Mailable; use PKP\mail\mailables\ReviewerResendRequest; use PKP\notification\PKPNotification; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use PKP\submission\reviewRound\ReviewRound; @@ -100,19 +102,21 @@ public function execute(...$functionArgs) ); // Add log - SubmissionLog::logEvent( - $request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_ASSIGN, - 'log.review.reviewerResendRequest', - [ - 'reviewAssignmentId' => $reviewAssignment->getId(), - 'reviewerName' => $reviewer->getFullName(), - 'submissionId' => $submission->getId(), - 'stageId' => $reviewAssignment->getStageId(), - 'round' => $reviewAssignment->getRound(), - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_ASSIGN, + 'userId' => Validation::loggedInAs() ?? $currentUser->getId(), + 'message' => 'log.review.reviewerResendRequest', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignmentId' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'submissionId' => $submission->getId(), + 'stageId' => $reviewAssignment->getStageId(), + 'round' => $reviewAssignment->getRound(), + ]); + Repo::eventLog()->add($eventLog); return true; } diff --git a/controllers/grid/users/reviewer/form/ReviewReminderForm.php b/controllers/grid/users/reviewer/form/ReviewReminderForm.php index 462a6e5eed3..3f0361046fe 100644 --- a/controllers/grid/users/reviewer/form/ReviewReminderForm.php +++ b/controllers/grid/users/reviewer/form/ReviewReminderForm.php @@ -18,18 +18,19 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\template\TemplateManager; use Illuminate\Support\Facades\Mail; use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; use PKP\facades\Locale; use PKP\form\Form; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\mailables\ReviewRemind; use PKP\mail\variables\ReviewAssignmentEmailVariable; use PKP\notification\PKPNotification; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; use Symfony\Component\Mailer\Exception\TransportException; @@ -153,18 +154,20 @@ public function execute(...$functionArgs) try { Mail::send($mailable); - SubmissionLog::logEvent( - $request, - $submission, - SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_REMIND, - 'submission.event.reviewer.reviewerReminded', - [ - 'recipientId' => $reviewer->getId(), - 'recipientName' => $reviewer->getFullName(), - 'senderId' => $user->getId(), - 'senderName' => $user->getFullName(), - ] - ); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_REMIND, + 'userId' => Validation::loggedInAs() ?? $user->getId(), + 'message' => 'submission.event.reviewer.reviewerReminded', + 'isTranslate' => 0, + 'dateLogged' => Core::getCurrentDate(), + 'recipientId' => $reviewer->getId(), + 'recipientName' => $reviewer->getFullName(), + 'senderId' => $user->getId(), + 'senderName' => $user->getFullName(), + ]); + Repo::eventLog()->add($eventLog); $reviewAssignment->setDateReminded(Core::getCurrentDate()); $reviewAssignment->stampModified(); diff --git a/controllers/grid/users/reviewer/form/UnassignReviewerForm.php b/controllers/grid/users/reviewer/form/UnassignReviewerForm.php index 8cf24480f52..1b4b74cd66a 100644 --- a/controllers/grid/users/reviewer/form/UnassignReviewerForm.php +++ b/controllers/grid/users/reviewer/form/UnassignReviewerForm.php @@ -17,17 +17,19 @@ use APP\core\Application; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use PKP\context\Context; +use PKP\core\Core; +use PKP\core\PKPApplication; use PKP\db\DAORegistry; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\mail\Mailable; use PKP\mail\mailables\ReviewerUnassign; use PKP\notification\NotificationDAO; use PKP\notification\PKPNotification; use PKP\plugins\Hook; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\reviewAssignment\ReviewAssignmentDAO; @@ -101,7 +103,21 @@ public function execute(...$functionArgs) $notificationMgr->createTrivialNotification($currentUser->getId(), PKPNotification::NOTIFICATION_TYPE_SUCCESS, ['contents' => $reviewAssignment->getDateConfirmed() ? __('notification.cancelledReviewer') : __('notification.removedReviewer')]); // Add log - SubmissionLog::logEvent($request, $submission, SubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_CLEAR, 'log.review.reviewCleared', ['reviewAssignmentId' => $reviewAssignment->getId(), 'reviewerName' => $reviewer->getFullName(), 'submissionId' => $submission->getId(), 'stageId' => $reviewAssignment->getStageId(), 'round' => $reviewAssignment->getRound()]); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_CLEAR, + 'userId' => Validation::loggedInAs() ?? $currentUser->getId(), + 'message' => 'log.review.reviewCleared', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'reviewAssignmentId' => $reviewAssignment->getId(), + 'reviewerName' => $reviewer->getFullName(), + 'submissionId' => $submission->getId(), + 'stageId' => $reviewAssignment->getStageId(), + 'round' => $reviewAssignment->getRound() + ]); + Repo::eventLog()->add($eventLog); return true; } diff --git a/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php b/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php index 1e02618c1a3..8f9b1333346 100644 --- a/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php +++ b/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php @@ -19,7 +19,6 @@ use APP\core\Application; use APP\core\Request; use APP\facades\Repo; -use APP\log\SubmissionEventLogEntry; use APP\notification\NotificationManager; use APP\submission\Submission; use Illuminate\Support\Facades\Mail; @@ -28,6 +27,7 @@ use PKP\controllers\grid\queries\traits\StageMailable; use PKP\controllers\grid\users\stageParticipant\form\AddParticipantForm; use PKP\controllers\grid\users\stageParticipant\form\PKPStageParticipantNotifyForm; +use PKP\core\Core; use PKP\core\JSONMessage; use PKP\core\PKPApplication; use PKP\core\PKPRequest; @@ -35,8 +35,8 @@ use PKP\linkAction\LinkAction; use PKP\linkAction\request\AjaxModal; use PKP\linkAction\request\RedirectAction; -use PKP\log\SubmissionLog; use PKP\notification\NotificationDAO; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\notification\PKPNotification; use PKP\security\authorization\WorkflowStageAccessPolicy; use PKP\security\Role; @@ -141,7 +141,7 @@ public function initialize($request, $args = null) )); $submission = $this->getSubmission(); $submissionId = $submission->getId(); - if (Validation::isLoggedInAs()) { + if (Validation::loggedInAs()) { $router = $request->getRouter(); $dispatcher = $router->getDispatcher(); $user = $request->getUser(); @@ -380,7 +380,19 @@ public function saveParticipant($args, $request) // Log addition. $assignedUser = Repo::user()->get($userId, true); - SubmissionLog::logEvent($request, $submission, SubmissionEventLogEntry::SUBMISSION_LOG_ADD_PARTICIPANT, 'submission.event.participantAdded', ['name' => $assignedUser->getFullName(), 'username' => $assignedUser->getUsername(), 'userGroupName' => $userGroup->getLocalizedName()]); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_ADD_PARTICIPANT, + 'userId' => Validation::loggedInAs() ?? $user->getId(), + 'message' => 'submission.event.participantAdded', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'userFullName' => $assignedUser->getFullName(), + 'username' => $assignedUser->getUsername(), + 'userGroupName' => $userGroup->getData('name') + ]); + Repo::eventLog()->add($eventLog); return \PKP\db\DAO::getDataChangedEvent($userGroupId); } else { @@ -442,7 +454,20 @@ public function deleteParticipant($args, $request) // Log removal. $assignedUser = Repo::user()->get($stageAssignment->getUserId(), true); $userGroup = Repo::userGroup()->get($stageAssignment->getUserGroupId()); - SubmissionLog::logEvent($request, $submission, SubmissionEventLogEntry::SUBMISSION_LOG_REMOVE_PARTICIPANT, 'submission.event.participantRemoved', ['name' => $assignedUser->getFullName(), 'username' => $assignedUser->getUsername(), 'userGroupName' => $userGroup->getLocalizedName()]); + + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => PKPSubmissionEventLogEntry::SUBMISSION_LOG_REMOVE_PARTICIPANT, + 'userId' => Validation::loggedInAs() ?? $request->getUser()->getId(), + 'message' => 'submission.event.participantRemoved', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate(), + 'userFullName' => $assignedUser->getFullName(), + 'username' => $assignedUser->getUsername(), + 'userGroupName' => $userGroup->getData('name') + ]); + Repo::eventLog()->add($eventLog); // Redraw the category return \PKP\db\DAO::getDataChangedEvent($stageAssignment->getUserGroupId()); diff --git a/controllers/grid/users/stageParticipant/StageParticipantGridRow.php b/controllers/grid/users/stageParticipant/StageParticipantGridRow.php index 014c1e8a1f6..b4d280940f9 100644 --- a/controllers/grid/users/stageParticipant/StageParticipantGridRow.php +++ b/controllers/grid/users/stageParticipant/StageParticipantGridRow.php @@ -110,7 +110,7 @@ public function initialize($request, $template = null) $user = $request->getUser(); if ( - !Validation::isLoggedInAs() && + !Validation::loggedInAs() && $user->getId() != $userId && Validation::getAdministrationLevel($userId, $user->getId()) === Validation::ADMINISTRATION_FULL ) { diff --git a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php index da93d6cde55..43428c254dc 100644 --- a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php +++ b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php @@ -30,15 +30,15 @@ use PKP\core\PKPRequest; use PKP\db\DAORegistry; use PKP\form\Form; -use PKP\log\EventLogEntry; +use PKP\log\event\EventLogEntry; use PKP\log\SubmissionEmailLogDAO; use PKP\log\SubmissionEmailLogEntry; -use PKP\log\SubmissionLog; use PKP\note\NoteDAO; use PKP\notification\NotificationDAO; use PKP\notification\PKPNotification; use PKP\query\QueryDAO; use PKP\security\Role; +use PKP\security\Validation; use Symfony\Component\Mailer\Exception\TransportException; class PKPStageParticipantNotifyForm extends Form @@ -380,10 +380,19 @@ private function _addAssignmentTaskNotification($request, $type, $userId, $submi */ public function _logEventAndCreateNotification($request, $submission) { - SubmissionLog::logEvent($request, $submission, EventLogEntry::SUBMISSION_LOG_MESSAGE_SENT, 'informationCenter.history.messageSent'); + $currentUser = $request->getUser(); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'eventType' => EventLogEntry::SUBMISSION_LOG_MESSAGE_SENT, + 'userId' => Validation::loggedInAs() ?? $currentUser->getId(), + 'message' => 'informationCenter.history.messageSent', + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); // Create trivial notification. - $currentUser = $request->getUser(); $notificationMgr = new NotificationManager(); $notificationMgr->createTrivialNotification($currentUser->getId(), PKPNotification::NOTIFICATION_TYPE_SUCCESS, ['contents' => __('stageParticipants.history.messageSent')]); } diff --git a/controllers/informationCenter/FileInformationCenterHandler.php b/controllers/informationCenter/FileInformationCenterHandler.php index e0c15acb81a..ecf4e8eddb7 100644 --- a/controllers/informationCenter/FileInformationCenterHandler.php +++ b/controllers/informationCenter/FileInformationCenterHandler.php @@ -25,7 +25,7 @@ use PKP\core\PKPApplication; use PKP\core\PKPRequest; use PKP\db\DAORegistry; -use PKP\log\EventLogEntry; +use PKP\log\event\EventLogEntry; use PKP\notification\PKPNotification; use PKP\security\authorization\WorkflowStageAccessPolicy; use PKP\security\Role; @@ -172,7 +172,7 @@ public function saveNote($args, $request) $notesForm->execute(); // Save to event log - $this->_logEvent($request, $this->submissionFile, EventLogEntry::SUBMISSION_LOG_NOTE_POSTED, 'PKP\log\SubmissionFileLog'); + $this->_logEvent($request, $this->submissionFile, EventLogEntry::SUBMISSION_LOG_NOTE_POSTED, PKPApplication::ASSOC_TYPE_SUBMISSION_FILE); $user = $request->getUser(); $notificationManager = new NotificationManager(); @@ -256,11 +256,12 @@ public function setupTemplate($request) { $templateMgr = TemplateManager::getManager($request); - // Get the latest history item to display in the header - $submissionFileEventLogDao = DAORegistry::getDAO('SubmissionFileEventLogDAO'); /** @var SubmissionFileEventLogDAO $submissionFileEventLogDao */ - $fileEvents = $submissionFileEventLogDao->getBySubmissionFileId($this->submissionFile->getId()); - $lastEvent = $fileEvents->next(); - if (isset($lastEvent)) { + $lastEvent = Repo::eventLog()->getCollector() + ->filterByAssoc(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE, [$this->submissionFile->getId()]) + ->getMany() + ->first(); + + if (!is_null($lastEvent)) { $templateMgr->assign('lastEvent', $lastEvent); // Get the user who created the last event. diff --git a/controllers/informationCenter/InformationCenterHandler.php b/controllers/informationCenter/InformationCenterHandler.php index 4e32c65c1e7..458335eadd0 100644 --- a/controllers/informationCenter/InformationCenterHandler.php +++ b/controllers/informationCenter/InformationCenterHandler.php @@ -17,20 +17,21 @@ namespace PKP\controllers\informationCenter; use APP\core\Application; +use APP\facades\Repo; use APP\handler\Handler; use APP\notification\NotificationManager; use APP\submission\Submission; use APP\template\TemplateManager; +use PKP\core\Core; use PKP\core\JSONMessage; use PKP\core\PKPRequest; use PKP\db\DAORegistry; -use PKP\log\EventLogEntry; -use PKP\log\SubmissionFileLog; -use PKP\log\SubmissionLog; +use PKP\log\event\EventLogEntry; use PKP\note\NoteDAO; use PKP\notification\PKPNotification; use PKP\security\authorization\SubmissionAccessPolicy; use PKP\security\Role; +use PKP\security\Validation; use PKP\submissionFile\SubmissionFile; abstract class InformationCenterHandler extends Handler @@ -190,13 +191,13 @@ public function _getLinkParams() /** * Log an event for this file or submission - * - * @param PKPRequest $request - * @param Submission|SubmissionFile $object - * @param int $eventType SUBMISSION_LOG_... - * @param SubmissionLog|SubmissionFileLog $logClass */ - public function _logEvent($request, $object, $eventType, $logClass) + public function _logEvent( + PKPRequest $request, + Submission|SubmissionFile $object, + int $eventType, //SUBMISSION_LOG_... const + int $assocType // PKPApplication::ASSOC_TYPE_SUBMISSION_FILE || PKPApplication::ASSOC_TYPE_SUBMISSION + ) { // Get the log event message switch ($eventType) { @@ -210,7 +211,16 @@ public function _logEvent($request, $object, $eventType, $logClass) assert(false); } - $logClass::logEvent($request, $object, $eventType, $logMessage); + $eventLog = Repo::eventLog()->newDataObject([ + 'assocType' => $assocType, + 'assocId' => $object->getId(), + 'eventType' => $eventType, + 'userId' => Validation::loggedInAs() ?? $request->getUser()->getId(), + 'message' => $logMessage, + 'isTranslated' => false, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); } public function setupTemplate($request) diff --git a/controllers/informationCenter/SubmissionInformationCenterHandler.php b/controllers/informationCenter/SubmissionInformationCenterHandler.php index 3fef0022142..90a74e4cb0d 100644 --- a/controllers/informationCenter/SubmissionInformationCenterHandler.php +++ b/controllers/informationCenter/SubmissionInformationCenterHandler.php @@ -21,7 +21,8 @@ use APP\template\TemplateManager; use PKP\controllers\informationCenter\form\NewSubmissionNoteForm; use PKP\core\JSONMessage; -use PKP\log\EventLogEntry; +use PKP\core\PKPApplication; +use PKP\log\event\EventLogEntry; use PKP\notification\PKPNotification; use PKP\security\Role; @@ -113,7 +114,7 @@ public function saveNote($args, $request) $notesForm->execute(); // Save to event log - $this->_logEvent($request, $this->_submission, EventLogEntry::SUBMISSION_LOG_NOTE_POSTED, 'PKP\log\SubmissionLog'); + $this->_logEvent($request, $this->_submission, EventLogEntry::SUBMISSION_LOG_NOTE_POSTED, PKPApplication::ASSOC_TYPE_SUBMISSION); $user = $request->getUser(); $notificationManager = new NotificationManager(); diff --git a/controllers/wizard/fileUpload/FileUploadWizardHandler.php b/controllers/wizard/fileUpload/FileUploadWizardHandler.php index 7a7a6e48c2b..d587d3a3c97 100644 --- a/controllers/wizard/fileUpload/FileUploadWizardHandler.php +++ b/controllers/wizard/fileUpload/FileUploadWizardHandler.php @@ -418,13 +418,19 @@ public function uploadFile($args, $request) return new JSONMessage(false, $uploadForm->fetch($request)); } + $submissionFileId = $uploadForm->getRevisedFileId(); + // Store the data of the submission file before it's replaced by the revised file + if ($submissionFileId) { + $originalFile = Repo::submissionFile()->get($submissionFileId); + } + $uploadedFile = $uploadForm->execute(); /** @var SubmissionFile $uploadedFile */ if (!is_a($uploadedFile, 'SubmissionFile')) { return new JSONMessage(false, __('common.uploadFailed')); } // Retrieve file info to be used in a JSON response. - $uploadedFileInfo = $this->_getUploadedFileInfo($uploadedFile); + $uploadedFileInfo = $this->_getUploadedFileInfo($uploadedFile, $originalFile ?? null); $reviewRound = $this->getReviewRound(); // Advance to the next step (i.e. meta-data editing). @@ -552,9 +558,9 @@ public function _onlyNumbersDiffer($a, $b) * * @return array */ - public function _getUploadedFileInfo($uploadedFile) + public function _getUploadedFileInfo(SubmissionFile $uploadedFile, ?SubmissionFile $originalFile = null) { - return [ + $uploadedFile = [ 'uploadedFile' => [ 'id' => $uploadedFile->getId(), 'fileId' => $uploadedFile->getData('fileId'), @@ -562,5 +568,15 @@ public function _getUploadedFileInfo($uploadedFile) 'genreId' => $uploadedFile->getGenreId(), ] ]; + + if ($originalFile) { + $uploadedFile['uploadedFile']['originalFile'] = [ + 'fileId' => $originalFile->getData('fileId'), + 'name' => $originalFile->getData('name'), + 'uploaderUserId' => $originalFile->getData('uploaderUserId'), + ]; + } + + return $uploadedFile; } } diff --git a/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js b/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js index c5325fbd101..a5b2a5543e0 100644 --- a/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js +++ b/js/controllers/wizard/fileUpload/FileUploadWizardHandler.js @@ -39,6 +39,7 @@ this.deleteUrl_ = options.deleteUrl; this.metadataUrl_ = options.metadataUrl; this.finishUrl_ = options.finishUrl; + this.cancelUrl_ = options.cancelUrl; // Bind events of the nested upload forms. this.bind('fileUploaded', this.handleFileUploaded); @@ -65,7 +66,7 @@ /** - * The URL to be called when a cancel event occurs. + * The URL to be called when a delete event occurs. * @private * @type {string} */ @@ -90,6 +91,14 @@ $.pkp.controllers.wizard.fileUpload.FileUploadWizardHandler. prototype.finishUrl_ = ''; + /** + * The URL to be called when a cancel event occurs. + * @private + * @type {string} + */ + $.pkp.controllers.wizard.fileUpload.FileUploadWizardHandler. + prototype.cancelUrl_ = ''; + /** * Information about the uploaded file (once there is one). @@ -99,7 +108,13 @@ $.pkp.controllers.wizard.fileUpload.FileUploadWizardHandler. prototype.uploadedFile_ = null; - + /** + * Information about the file being revised. + * @private + * @type {{fileId: number, name: string, uploaderUserId: number}} + */ + $.pkp.controllers.wizard.fileUpload.FileUploadWizardHandler. + prototype.originalFile_ = null; // // Public methods // @@ -243,7 +258,8 @@ this.uploadedFile_.csrfToken = this.csrfToken_; // Authorization policy expects to find the submissionFileId para this.uploadedFile_.submissionFileId = this.uploadedFile_.id; - $.post(this.deleteUrl_, this.uploadedFile_, + this.uploadedFile_.originalFile = this.originalFile_; + $.post(this.cancelUrl_, this.uploadedFile_, $.pkp.classes.Helper.curry(this.wizardCancelSuccess, this, wizardElement, event), 'json'); @@ -298,7 +314,13 @@ $.pkp.controllers.wizard.fileUpload.FileUploadWizardHandler. prototype.handleFileUploaded = function(callingForm, event, uploadedFile) { - // Save the uploaded file information. + // Keep the original file data to restore if the wizard is canceled + if (this.originalFile_ === null) { + this.originalFile_ = uploadedFile.originalFile; + } + delete uploadedFile.originalFile; + + // Save the uploaded file information this.uploadedFile_ = uploadedFile; }; @@ -348,8 +370,8 @@ $.pkp.controllers.wizard.fileUpload.FileUploadWizardHandler. prototype.startWizard = function() { - // Reset the uploaded file. - this.uploadedFile_ = null; + // Reset the uploaded and original file. + this.uploadedFile_ = this.originalFile_= null; this.parent('startWizard'); }; diff --git a/schemas/eventLog.json b/schemas/eventLog.json new file mode 100644 index 00000000000..0fedc5f2f52 --- /dev/null +++ b/schemas/eventLog.json @@ -0,0 +1,251 @@ +{ + "title": "Event Log", + "description": "Logged action taken regarding the submission.", + "required": [ + "assocType", + "assocId", + "dateLogged" + ], + "properties": { + "assocId": { + "type": "integer", + "description": "The submission or submission file ID.", + "apiSummary": true + }, + "assocType": { + "type": "integer", + "description": "The assoc object. This should always be `ASSOC_TYPE_SUBMISSION` or `ASSOC_TYPE_SUBMISSION_FILE`.", + "apiSummary": true + }, + "copyrightNotice": { + "type": "string", + "description": "The context derived copyright notice confirmed for the submission", + "multilingual": true, + "validation": [ + "nullable" + ] + }, + "data": { + "type": "Object", + "description": "Additional data attached to this event log.", + "validation": [ + "nullable" + ] + }, + "dateLogged": { + "type": "string", + "description": "The date this event was recorded.", + "apiSummary": true, + "writeDisabledInApi": true, + "validation": [ + "date_format:Y-m-d H:i:s" + ] + }, + "decision": { + "type": "integer", + "description": "The decision type identifier. One of the Decision::* constants.", + "validation": [ + "nullable", + "min:1" + ] + }, + "editorId": { + "type": "integer", + "description": "The user ID of the editor associated with the event.", + "validation": [ + "nullable", + "min:1" + ] + + }, + "editorName": { + "type": "string", + "description": "The full name of the editor associated with the event, e.g., who made a decision or recommendation.", + "validation": [ + "nullable" + ] + }, + "eventType": { + "type": "integer", + "description": "The type of the event. One of the `SUBMISSION_LOG_` constants.", + "apiSummary": true, + "validation": [ + "nullable" + ] + }, + "fileId": { + "type": "integer", + "description": "The ID of the file associated with the submission file", + "validation": [ + "nullable", + "min:1" + ] + }, + "filename": { + "type": "string", + "description": "The name of the file associated with the submission file", + "multilingual": true, + "validation": [ + "nullable" + ] + }, + "fileStage": { + "type": "integer", + "description": "One of the `SubmissionFile::SUBMISSION_FILE_` constants", + "validation": [ + "nullable", + "in:2,3,4,5,6,7,8,9,10,11,13,15,17,18,19,20" + ] + }, + "id": { + "type": "integer", + "description": "The id of the event log.", + "readOnly": true, + "apiSummary": true + }, + "isTranslated": { + "type": "boolean", + "description": "Whether the message is translated.", + "validation": [ + "nullable" + ] + }, + "message": { + "type": "string", + "description": "Custom log message, either a locale key or literal string.", + "apiSummary": true, + "validation": [ + "nullable" + ] + }, + "recipientCount": { + "type": "integer", + "description": "The number of users indicated as recipients of an email.", + "validation": [ + "nullable" + ] + }, + "recipientId": { + "type": "integer", + "description": "The user ID of the email recipient.", + "validation": [ + "nullable", + "min:1" + ] + }, + "recipientName": { + "type": "string", + "description": "The full name of the email recipient.", + "validation": [ + "nullable" + ] + }, + "reviewAssignmentId": { + "type": "integer", + "description": "The ID of associated review assignment.", + "validation": [ + "nullable", + "min:1" + ] + }, + "reviewDueDate": { + "type": "string", + "description": "Date when the reviewer should accept or decline the assignment. Uses context's date and time format.", + "validation": [ + "nullable" + ] + }, + "reviewerName": { + "type": "string", + "description": "The full name of the reviewer associated with the event.", + "validation": [ + "nullable" + ] + }, + "round": { + "type": "integer", + "description": "The number of the review round.", + "validation": [ + "nullable", + "min:1" + ] + }, + "senderId": { + "type": "integer", + "description": "The user ID of the email sender.", + "validation": [ + "nullable", + "min:1" + ] + }, + "senderName": { + "type": "string", + "description": "The full name of the email sender.", + "multilingual": true, + "validation": [ + "nullable" + ] + }, + "stageId": { + "type": "integer", + "description": "The workflow stage ID associated submission was in when this event was logged. One of the `WORKFLOW_STAGE_ID_` constants", + "validation": [ + "nullable", + "in:1,2,3,4,5" + ] + }, + "sourceSubmissionFileId": { + "type": "integer", + "description": "The ID of the source file from which the current submission file was promoted to another stage", + "validation": [ + "nullable", + "min:1" + ] + }, + "subject": { + "type": "string", + "description": "The subject of the sent email.", + "validation": [ + "nullable" + ] + }, + "submissionId": { + "type": "integer", + "description": "The ID of the submission associates with the logged event.", + "validation": [ + "nullable", + "min:1" + ] + }, + "userFullName": { + "type": "string", + "description": "The full name of the user/author associated with the event, e.g., confirming the copyright during submission.", + "validation": [ + "nullable" + ] + }, + "userGroupName": { + "type": "string", + "description": "The name of the role the user is in.", + "multilingual": true, + "validation": [ + "nullable" + ] + }, + "userId": { + "type": "integer", + "description": "The ID of the user who triggered the event", + "validation": [ + "nullable", + "min:1" + ] + }, + "username": { + "type": "string", + "description": "The username of the user/author associated with the event.", + "validation": [ + "nullable" + ] + } + } +} \ No newline at end of file diff --git a/schemas/userGroup.json b/schemas/userGroup.json index 3118501d220..fd2d25f1600 100644 --- a/schemas/userGroup.json +++ b/schemas/userGroup.json @@ -4,13 +4,13 @@ "properties": { "abbrev": { "type": "string", - "description": "An optional field for contributors to specify how they prefer to be identified in this publication.", + "description": "The short name of the user group.", "apiSummary": true, "multilingual": true }, "name": { "type": "string", - "description": "An optional field for contributors to specify how they prefer to be identified in this publication.", + "description": "The name of the user group.", "apiSummary": true, "multilingual": true }, diff --git a/templates/controllers/wizard/fileUpload/fileUploadWizard.tpl b/templates/controllers/wizard/fileUpload/fileUploadWizard.tpl index a8ba2f6a87b..b691241f7eb 100644 --- a/templates/controllers/wizard/fileUpload/fileUploadWizard.tpl +++ b/templates/controllers/wizard/fileUpload/fileUploadWizard.tpl @@ -26,7 +26,8 @@ finishButtonText: {translate|json_encode key="common.complete"}, deleteUrl: {url|json_encode component="api.file.ManageFileApiHandler" op="deleteFile" submissionId=$submissionId stageId=$stageId fileStage=$fileStage suppressNotification=true escape=false}, metadataUrl: {url|json_encode op="editMetadata" submissionId=$submissionId stageId=$stageId reviewRoundId=$reviewRoundId fileStage=$fileStage assocType=$assocType assocId=$assocId queryId=$queryId escape=false}, - finishUrl: {url|json_encode op="finishFileSubmission" submissionId=$submissionId stageId=$stageId reviewRoundId=$reviewRoundId fileStage=$fileStage assocType=$assocType assocId=$assocId queryId=$queryId escape=false} + finishUrl: {url|json_encode op="finishFileSubmission" submissionId=$submissionId stageId=$stageId reviewRoundId=$reviewRoundId fileStage=$fileStage assocType=$assocType assocId=$assocId queryId=$queryId escape=false}, + cancelUrl: {url|json_encode component="api.file.ManageFileApiHandler" op="cancelFileUpload" submissionId=$submissionId stageId=$stageId fileStage=$fileStage escape=false} {rdelim} ); {rdelim});