diff --git a/api/v1/submissions/PKPSubmissionHandler.php b/api/v1/submissions/PKPSubmissionHandler.php index 2b5142313a9..17989243234 100644 --- a/api/v1/submissions/PKPSubmissionHandler.php +++ b/api/v1/submissions/PKPSubmissionHandler.php @@ -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\PKPSubmission; @@ -708,17 +710,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' => 0, + '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/category/Repository.php b/classes/category/Repository.php index 56a492c7293..f37f9b36a4b 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 diff --git a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php index 9f0fefd5200..c938ce35e44 100644 --- a/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php +++ b/classes/controllers/grid/users/reviewer/PKPReviewerGridHandler.php @@ -45,6 +45,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; @@ -57,6 +58,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; @@ -675,17 +677,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' => 0, + 'dateLogged' => Core::getCurrentDate(), + 'editorName' => $user->getFullName(), + 'submissionId' => $submission->getId(), + 'round' => $reviewAssignment->getRound(), + ]); + Repo::eventLog()->add($eventLog); return \PKP\db\DAO::getDataChangedEvent($reviewAssignment->getId()); } @@ -738,17 +742,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' => 0, + '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 9eeea0fbeb1..cc7ebb31a1b 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 246f5d10a79..7bc7a92d54d 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 aba60b807f2..c51a86ea509 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\submission\reviewRound\ReviewRoundDAO; use PKP\submissionFile\SubmissionFile; @@ -226,20 +228,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' => 0, + '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..34db1fd943a 100644 --- a/classes/decision/types/traits/NotifyReviewers.php +++ b/classes/decision/types/traits/NotifyReviewers.php @@ -20,11 +20,14 @@ 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' => 0, + '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/log/EventLogDAO.php b/classes/log/EventLogDAO.php deleted file mode 100644 index daa7b24746e..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 object $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 ba935de5c87..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 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 2349f5c58f4..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 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 d6fa2bbeffb..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 9c5bae35c71..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..7705871a414 --- /dev/null +++ b/classes/log/event/Collector.php @@ -0,0 +1,98 @@ +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); + } + + public function filterByAssocType(?int $assocType) + { + $this->assocType = $assocType; + return $this; + } + + public function filterByAssocId(?array $assocIds) + { + $this->assocIds = $assocIds; + return $this; + } + + public function filterByUserIds(?array $userIds) + { + $this->userIds = $userIds; + 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..6cfc8be21f1 --- /dev/null +++ b/classes/log/event/DAO.php @@ -0,0 +1,302 @@ + 'log_id', + 'assocType' => 'assoc_type', + 'assocId' => 'assoc_id', + 'userId' => 'user_id', + 'dateLogged' => 'date_logged', + 'eventType' => 'event_type', + 'message' => 'message', + 'isTranslated' => 'is_translated', + ]; + + public function __construct() + { + $this->deprecatedDao = new \PKP\db\DAO(); + + // Overriding schema service to allow adding custom data to the event log + $this->schemaService = new class extends PKPSchemaService { + /** + * Schema for the custom data to be recorded in the event log + * [ + * 'propName' => [ + * 'type' => ... + * 'multilingual' => ... (optional) + * ] + */ + protected ?array $customProps = null; + + public function setCustomProps(array $props) + { + $cleanProps = $this->validateCustomProps($props); + $this->customProps = $cleanProps; + } + + public function getCustomProps(): ?array + { + return $this->customProps; + } + + /** + * Ensures custom props don't override original schema props. + * Only "type" and "multilingual" flags are allowed + */ + protected function validateCustomProps(array $props): array + { + // Check if property with specified name already exists + $schema = $this->get(PKPSchemaService::SCHEMA_EVENT_LOG); + if (!empty($schema->properties)) { + foreach ($schema->properties as $propName => $propSchema) { + if (array_key_exists($propName, $props)) { + unset($props[$propName]); + } + } + } + + // Ensure that unsupported props aren't get passed + $cleanProps = []; + foreach ($props as $name => $settings) { + $type = $settings['type'] ?? null; + if (!$type) { + continue; + } + $cleanProps[$name] = ['type' => $settings['type']]; + + $multilingual = $settings['multilingual'] ?? null; + if ($multilingual) { + $cleanProps[$name] = array_merge($cleanProps[$name], ['multilingual' => $settings['multilingual']]); + } + } + + return $cleanProps; + } + + /** + * Add custom properties to schema dynamically + */ + public function get($schemaName, $forceReload = false) + { + $schema = parent::get($schemaName, $forceReload); + if (!$this->getCustomProps()) { + return $schema; + } + + $customSchema = new \stdClass(); + $customSchema->properties = json_decode(json_encode($this->getCustomProps())); + $schema = $this->merge($schema, $customSchema); + $this->_schemas[$schemaName] = $schema;; + return $schema; + } + }; + } + + /** + * 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() + * + * Allows inserting event log with dynamically defined custom properties. + * Schema supports only 'type' and 'multilingual' flags; example of the expected format: + * [ + * 'name' => [ + * 'type' => 'string', + * 'multilingual' => true + * ] + * ] + */ + public function insert(EventLogEntry $eventLog, array $customPropsSchema = []): int + { + $this->schemaService->setCustomProps($customPropsSchema); + $id = parent::_insert($eventLog); + + $customProps = $this->schemaService->getCustomProps(); + if (!$customProps) { + return $id; + } + + foreach ($customProps as $propName => $propFlags) { + $this->setSettingType($propName, $propFlags['type'], $id); + } + + return $id; + } + + /** + * @copydoc EntityDAO::update() + * See self::insert() for custom props usage + */ + public function update(EventLogEntry $eventLog, array $customPropsSchema = []) + { + $this->schemaService->setCustomProps($customPropsSchema); + parent::_update($eventLog); + + $customProps = $this->schemaService->getCustomProps(); + if (!$customProps) { + return; + } + + foreach ($customProps as $propName => $propFlags) { + $this->setSettingType($propName, $propFlags['type'], $eventLog->getId()); + } + } + + /** + * @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]); + } + + /** + * Record setting type for custom props + */ + protected function setSettingType(string $propName, string $propType, int $objectId) + { + DB::table($this->settingsTable) + ->where('log_id', $objectId) + ->where('setting_name', $propName) + ->update([ + 'setting_type' => $this->deprecatedDao->getType($propType) + ]); + } +} \ 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 d3538500713..68994fb8a34 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, array $allowedLocales, string $primaryLocale): array + { + $validator = ValidatorFactory::make( + $props, + $this->schemaService->getValidationRules($this->dao->schema, $allowedLocales) + ); + + // Check required fields if we're adding a context + 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, array $customPropsSchema = []): int + { + $id = $this->dao->insert($logEntry, $customPropsSchema); + 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::update() */ + public function edit(EventLogEntry $logEntry, array $params, $customPropsSchema = []) + { + $newLogEntry = clone $logEntry; + $newLogEntry->setAllData(array_merge($newLogEntry->_data, $params)); + + Hook::call('EventLog::edit', [$newLogEntry, $logEntry, $params]); + + $this->dao->update($newLogEntry, $customPropsSchema); + } + + /** @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..6d403cca05b 100644 --- a/classes/migration/install/LogMigration.php +++ b/classes/migration/install/LogMigration.php @@ -31,7 +31,7 @@ public function up(): void $table->bigInteger('assoc_type'); $table->bigInteger('assoc_id'); - $table->bigInteger('user_id'); + $table->bigInteger('user_id')->nullable(); $table->foreign('user_id')->references('user_id')->on('users')->onDelete('cascade'); $table->index(['user_id'], 'event_log_user_id'); @@ -49,10 +49,11 @@ 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->mediumText('setting_value')->nullable()->default(null); $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..031ee766ead --- /dev/null +++ b/classes/migration/upgrade/v3_4_0/I8933_EventLogLocalized.php @@ -0,0 +1,243 @@ +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->string('setting_type', 6)->default(null)->change(); + }); + + // Events can be triggered without a user, e.g., in schedule tasks + Schema::table('event_log', function (Blueprint $table) { + $table->dropIndex('event_log_user_id'); + $table->bigInteger('user_id')->nullable()->change(); + $table->index(['user_id'], 'event_log_user_id'); + }); + + $this->fixConflictingSubmissionLogConstants(); + + // Rename ambiguous settings + $this->renameSettings(); + + // Localize existing submission file name entries + $sitePrimaryLocale = DB::table('site')->value('primary_locale'); + DB::table('event_log_settings') + ->where('setting_name', 'filename') + ->lazyById(1000, 'event_log_setting_id') + ->each(function (object $row) use ($sitePrimaryLocale) { + // Check event type + $eventType = DB::table('event_log') + ->where('log_id', $row->log_id)->value('event_type'); + if (!$this->isEventTypeToMigrateFileName($eventType)) { + return; + } + + // Determine locale + $locale = $this->getContextPrimaryLocale($row, $sitePrimaryLocale); + if (!$locale) { + return; + } + DB::table('event_log_settings') + ->where('event_log_setting_id', $row->event_log_setting_id) + ->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): ?string + { + // Find correspondent submission ID to set the correct locale + $submissionFileId = DB::table('event_log_settings') + ->where('log_id', $row->log_id) + ->where('setting_name', 'submissionFileId') + ->value('setting_value'); + + // Entry isn't related to the submission file + if (!$submissionFileId) { + return null; + } + + $submissionId = DB::table('submission_files') + ->where('submission_file_id', $submissionFileId) + ->value('submission_id'); + + // Submission removed? + if (!$submissionId) { + return $sitePrimaryLocale; + } + + $contextId = DB::table('submissions')->where('submission_id', $submissionId); + + if (!$contextId) { + return null; + } + + return DB::table($this->getContextTable())->value('primary_locale'); + } + + /** + * Event types that record submission file name + */ + protected function isEventTypeToMigrateFileName(string $typeToCheck): bool + { + $eventTypes = [ + SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_UPLOAD, + SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT, + SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD, + ]; + + return in_array($typeToCheck, $eventTypes); + } + + /** + * Rename setting name to avoid ambiguity in the event log schema + */ + protected function renameSettings() + { + DB::table('event_log') + ->whereIn('event_type', [$this->mapSettings()->keys()->all()]) + ->each(function (object $row) { + + // resolve conflict between 'name' and 'originalFileName' + if (in_array( + $row->event_type, + [SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD, SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT]) + ) { + if (DB::table('event_log_settings')->where('log_id', $row->log_id)->where('setting_name', 'name')->exists()) { + DB::table('event_log_settings') + ->where('log_id', $row->log_id) + ->where('setting_name', 'originalFileName') + ->delete(); + } + } + + // just rename other settings + $oldNewSettingNames = $this->mapSettings()->get($row->event_type); + foreach ($oldNewSettingNames as $oldSettingName => $newSettingName) { + DB::table('event_log_settings') + ->where('log_id', $row->log_id) + ->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([ + PKPSubmissionEventLogEntry::SUBMISSION_LOG_COPYRIGHT_AGREED => [ + 'name' => 'userFullName' + ], + PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_CONFIRMED => [ + 'userName' => 'editorName' + ], + PKPSubmissionEventLogEntry::SUBMISSION_LOG_REVIEW_SET_DUE_DATE => [ + 'dueDate' => 'reviewDueDate' + ], + 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' + */ + SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_REVISION_UPLOAD => [ + 'name' => 'filename', + 'originalFileName' => 'filename' + ], + SubmissionFileEventLogEntry::SUBMISSION_LOG_FILE_EDIT => [ + 'name' => 'filename', + 'originalFileName' => 'filename' + ], + PKPSubmissionEventLogEntry::SUBMISSION_LOG_ADD_PARTICIPANT => [ + 'name' => 'userFullName' + ], + 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..1a41886bb78 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' => 0, + 'dateLogged' => Core::getCurrentDate(), + ]); + Repo::eventLog()->add($eventLog); } } diff --git a/classes/publication/Repository.php b/classes/publication/Repository.php index 18406e512d2..b95cc1056d8 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,17 +25,17 @@ 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; -use PKP\user\User; use PKP\userGroup\UserGroup; use PKP\validation\ValidatorFactory; @@ -317,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); @@ -349,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' => 0, + 'dateLogged' => Core::getCurrentDate(), + ]); + Repo::eventLog()->add($eventLog); return $newPublication->getId(); } @@ -358,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')); @@ -389,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' => 0, + 'dateLogged' => Core::getCurrentDate(), + ]); + Repo::eventLog()->add($eventLog); return $newPublication; } @@ -470,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' => 0, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); // Mark DOIs stale (if applicable). if ($newPublication->getData('status') === Submission::STATUS_PUBLISHED) { @@ -557,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' => 0, + 'dateLogged' => Core::getCurrentDate() + ]); + Repo::eventLog()->add($eventLog); Hook::call( 'Publication::unpublish', diff --git a/classes/security/Validation.php b/classes/security/Validation.php index 335c1cb5bd9..96aa911e6ea 100644 --- a/classes/security/Validation.php +++ b/classes/security/Validation.php @@ -404,19 +404,18 @@ 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; } /** diff --git a/classes/services/PKPNavigationMenuService.php b/classes/services/PKPNavigationMenuService.php index ef2e5ec420e..fd4cc287c64 100755 --- a/classes/services/PKPNavigationMenuService.php +++ b/classes/services/PKPNavigationMenuService.php @@ -151,7 +151,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 3f8fb39ba27..dc1209cd9b1 100644 --- a/classes/services/PKPSchemaService.php +++ b/classes/services/PKPSchemaService.php @@ -42,6 +42,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 f7fcef7caff..2bf2dde0779 100644 --- a/classes/submission/DAO.php +++ b/classes/submission/DAO.php @@ -24,6 +24,7 @@ use PKP\core\EntityDAO; use PKP\core\traits\EntityWithParent; use PKP\db\DAORegistry; +use PKP\log\event\EventLogEntry; use PKP\services\PKPSchemaService; class DAO extends EntityDAO @@ -282,8 +283,13 @@ 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() + ->filterByAssocType(Application::ASSOC_TYPE_SUBMISSION) + ->filterByAssocId([$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 3ab90313b0d..4868d80f6a8 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\reviewRound\ReviewRoundDAO; @@ -108,12 +108,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' => 0, + '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'); @@ -124,7 +138,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')] ); @@ -170,23 +184,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' => 0, + '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..3ebf3f9e009 100644 --- a/classes/submission/reviewer/ReviewerAction.php +++ b/classes/submission/reviewer/ReviewerAction.php @@ -24,16 +24,17 @@ 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 63017285103..3a019d23940 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,13 +27,14 @@ 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\NotificationSubscriptionSettingsDAO; use PKP\notification\PKPNotification; use PKP\reviewForm\ReviewFormElement; use PKP\reviewForm\ReviewFormResponse; use PKP\security\Role; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; use PKP\submission\SubmissionComment; @@ -233,19 +233,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' => 0, + '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 a0cd53d6f14..e0e32116dbe 100644 --- a/classes/submissionFile/Repository.php +++ b/classes/submissionFile/Repository.php @@ -28,15 +28,14 @@ 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; use PKP\plugins\Hook; 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; @@ -265,39 +264,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' => 0, ] - ); + )); + 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' => 0, ] - ); + )); + Repo::eventLog()->add($logEntry); // Update status and notifications when revisions have been uploaded if ($submissionFile->getData('fileStage') === SubmissionFile::SUBMISSION_FILE_REVIEW_REVISION || @@ -364,40 +359,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' => 0, + '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(), ] - ); + )); } /** @@ -507,20 +495,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' => 0, + 'dateLogged' => Core::getCurrentDate(), ] - ); + )); + Repo::eventLog()->add($logEntry); Hook::call('SubmissionFile::delete', [$submissionFile]); } @@ -837,4 +823,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..afb0d5bab7c 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' => 0, + '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 c49cf18593d..138c2013da7 100644 --- a/classes/template/PKPTemplateManager.php +++ b/classes/template/PKPTemplateManager.php @@ -380,7 +380,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 c2cd9418a22..4434fea989b 100644 --- a/classes/user/Repository.php +++ b/classes/user/Repository.php @@ -307,8 +307,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 a2103837028..948bb8047f7 100644 --- a/controllers/api/file/PKPManageFileApiHandler.php +++ b/controllers/api/file/PKPManageFileApiHandler.php @@ -25,7 +25,9 @@ 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\PKPNotification; use PKP\observers\events\MetadataChanged; use PKP\security\authorization\SubmissionFileAccessPolicy; @@ -90,50 +92,70 @@ 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); - } - - // Restore original submission file - Repo::submissionFile()->edit( - $submissionFile, - [ - 'fileId' => $originalFile['fileId'], - 'name' => $originalFile['name'], - 'uploaderUserId' => $originalFile['uploaderUserId'], - ] - ); - - // Remove uploaded file - Services::get('file')->delete($revisedFileId); - - $this->setupTemplate($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 = $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. @@ -242,4 +264,43 @@ 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() + ->filterByAssocType(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE) + ->filterByAssocId([$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 fd8c348b58f..a0769c922f0 100644 --- a/controllers/grid/eventLog/EventLogGridCellProvider.php +++ b/controllers/grid/eventLog/EventLogGridCellProvider.php @@ -22,8 +22,8 @@ use PKP\controllers\grid\GridColumn; use PKP\db\DAORegistry; use PKP\log\EmailLogEntry; -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\submissionFile\SubmissionFile; diff --git a/controllers/grid/eventLog/EventLogGridRow.php b/controllers/grid/eventLog/EventLogGridRow.php index 73b337eca1c..36fd015a272 100644 --- a/controllers/grid/eventLog/EventLogGridRow.php +++ b/controllers/grid/eventLog/EventLogGridRow.php @@ -23,8 +23,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\submissionFile\SubmissionFile; diff --git a/controllers/grid/eventLog/SubmissionEventLogGridHandler.php b/controllers/grid/eventLog/SubmissionEventLogGridHandler.php index 987480274d9..d207bafee36 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 PKP\controllers\grid\DateGridCellProvider; use PKP\controllers\grid\GridColumn; use PKP\controllers\grid\GridHandler; @@ -25,7 +26,7 @@ use PKP\core\PKPString; use PKP\db\DAORegistry; use PKP\log\EmailLogEntry; -use PKP\log\EventLogEntry; +use PKP\log\event\EventLogEntry; use PKP\security\authorization\internal\UserAccessibleWorkflowStageRequiredPolicy; use PKP\security\authorization\SubmissionAccessPolicy; use PKP\security\Role; @@ -197,12 +198,14 @@ 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() + ->filterByAssocType(PKPApplication::ASSOC_TYPE_SUBMISSION) + ->filterByAssocId([$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 b175696461b..4efddc9d4d6 100644 --- a/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.php +++ b/controllers/grid/eventLog/SubmissionFileEventLogGridHandler.php @@ -17,7 +17,8 @@ namespace PKP\controllers\grid\eventLog; use APP\core\Application; -use PKP\db\DAORegistry; +use APP\facades\Repo; +use PKP\core\PKPApplication; use PKP\security\authorization\SubmissionFileAccessPolicy; use PKP\security\Role; use PKP\submissionFile\SubmissionFile; @@ -112,14 +113,11 @@ 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() + ->filterByAssocType(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE) + ->filterByAssocId($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 197631ec31f..29ffcec044c 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; class ReinstateReviewerForm extends ReviewerNotifyActionForm @@ -85,7 +87,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' => 0, + '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 aff5b0e8e0b..52533f024be 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\reviewRound\ReviewRound; @@ -99,19 +101,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' => 0, + '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 6d86203ae82..790d8df9550 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 Symfony\Component\Mailer\Exception\TransportException; @@ -152,18 +153,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 b1f50aaa8e9..4ed786e65ca 100644 --- a/controllers/grid/users/reviewer/form/UnassignReviewerForm.php +++ b/controllers/grid/users/reviewer/form/UnassignReviewerForm.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\ReviewerUnassign; use PKP\notification\PKPNotification; use PKP\plugins\Hook; +use PKP\security\Validation; use PKP\submission\reviewAssignment\ReviewAssignment; class UnassignReviewerForm extends ReviewerNotifyActionForm @@ -99,7 +101,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' => 0, + '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 df9d435daf0..a6f2ce821db 100644 --- a/controllers/grid/users/stageParticipant/StageParticipantGridHandler.php +++ b/controllers/grid/users/stageParticipant/StageParticipantGridHandler.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 Illuminate\Support\Facades\Mail; @@ -27,13 +26,14 @@ 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\db\DAORegistry; use PKP\linkAction\LinkAction; use PKP\linkAction\request\AjaxModal; use PKP\linkAction\request\RedirectAction; -use PKP\log\SubmissionLog; +use PKP\log\event\PKPSubmissionEventLogEntry; use PKP\notification\PKPNotification; use PKP\security\authorization\WorkflowStageAccessPolicy; use PKP\security\Role; @@ -137,7 +137,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(); @@ -376,7 +376,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' => 0, + 'dateLogged' => Core::getCurrentDate(), + 'userFullName' => $assignedUser->getFullName(), + 'username' => $assignedUser->getUsername(), + 'userGroupName' => $userGroup->getData('name') + ]); + Repo::eventLog()->add($eventLog); return \PKP\db\DAO::getDataChangedEvent($userGroupId); } else { @@ -438,7 +450,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' => 0, + '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 cb67529167f..341f54bcd94 100644 --- a/controllers/grid/users/stageParticipant/StageParticipantGridRow.php +++ b/controllers/grid/users/stageParticipant/StageParticipantGridRow.php @@ -109,7 +109,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 01883d2d666..e73f98a3825 100644 --- a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php +++ b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php @@ -29,13 +29,13 @@ use PKP\core\PKPApplication; 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\notification\PKPNotification; use PKP\security\Role; +use PKP\security\Validation; use Symfony\Component\Mailer\Exception\TransportException; class PKPStageParticipantNotifyForm extends Form @@ -368,10 +368,21 @@ 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' => 0, + '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 ec62dc10680..af3d610de8c 100644 --- a/controllers/informationCenter/FileInformationCenterHandler.php +++ b/controllers/informationCenter/FileInformationCenterHandler.php @@ -24,7 +24,7 @@ use PKP\core\JSONMessage; use PKP\core\PKPApplication; 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; @@ -171,7 +171,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(); @@ -255,11 +255,13 @@ 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() + ->filterByAssocType(PKPApplication::ASSOC_TYPE_SUBMISSION_FILE) + ->filterByAssocId($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 f619e5cffd2..150d72a48eb 100644 --- a/controllers/informationCenter/InformationCenterHandler.php +++ b/controllers/informationCenter/InformationCenterHandler.php @@ -17,17 +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\event\EventLogEntry; 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 { @@ -186,13 +190,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) { @@ -206,7 +210,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' => 0, + '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/schemas/eventLog.json b/schemas/eventLog.json new file mode 100644 index 00000000000..22095293f20 --- /dev/null +++ b/schemas/eventLog.json @@ -0,0 +1,245 @@ +{ + "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" + ] + }, + "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", + "max:32" + ] + }, + "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 is in. 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 },