From b8bd6fd1b014bd572756bd46e8ccc74857f70c89 Mon Sep 17 00:00:00 2001 From: Kaitlin Newson Date: Mon, 23 Sep 2024 14:16:37 -0300 Subject: [PATCH] #10189 Convert Queries to an Eloquent Model (#10384) * pkp/pkp-lib#10189 Convert Queries to an Eloquent Model * pkp/pkp-lib#10189 Convert Queries to an Eloquent Model * pkp/pkp-lib#10189 Convert Queries to an Eloquent Model * pkp/pkp-lib#10189 cleanup indentation issues * pkp/pkp-lib#10189 review feedback * pkp/pkp-lib#10189 review feedback --- classes/controllers/grid/GridHandler.php | 25 +- classes/core/PKPApplication.php | 5 +- .../types/traits/IsRecommendation.php | 14 +- classes/facades/Repo.php | 12 +- classes/note/Note.php | 2 +- classes/note/Repository.php | 29 +- ...ingProductionStatusNotificationManager.php | 30 +- .../QueryNotificationManager.php | 34 +- classes/query/Query.php | 155 +++--- classes/query/QueryDAO.php | 490 ------------------ classes/query/QueryParticipant.php | 88 ++++ classes/query/Repository.php | 199 +++++++ .../authorization/NoteAccessPolicy.php | 15 +- .../QueryAssignedToUserAccessPolicy.php | 25 +- .../internal/QueryRequiredPolicy.php | 17 +- ...ubmissionFileAssignedQueryAccessPolicy.php | 28 +- classes/submission/DAO.php | 10 +- classes/submission/Repository.php | 9 +- classes/submission/maps/Schema.php | 9 +- classes/submissionFile/Repository.php | 11 +- ...ubmissionFilesCategoryGridDataProvider.php | 11 +- .../query/ManageQueryNoteFilesGridHandler.php | 9 +- .../query/QueryNoteFilesGridDataProvider.php | 11 +- .../files/query/QueryNoteFilesGridHandler.php | 6 +- .../NotificationsGridCellProvider.php | 15 +- .../grid/queries/QueriesAccessHelper.php | 30 +- .../grid/queries/QueriesGridCellProvider.php | 15 +- .../grid/queries/QueriesGridHandler.php | 105 ++-- .../grid/queries/QueryNotesGridHandler.php | 25 +- .../grid/queries/QueryNotesGridRow.php | 3 +- .../grid/queries/QueryTitleGridColumn.php | 9 +- controllers/grid/queries/form/QueryForm.php | 116 +++-- .../grid/queries/form/QueryNoteForm.php | 37 +- .../form/PKPStageParticipantNotifyForm.php | 45 +- .../controllers/grid/queries/readQuery.tpl | 6 +- 35 files changed, 719 insertions(+), 931 deletions(-) delete mode 100644 classes/query/QueryDAO.php create mode 100644 classes/query/QueryParticipant.php create mode 100644 classes/query/Repository.php diff --git a/classes/controllers/grid/GridHandler.php b/classes/controllers/grid/GridHandler.php index c897f9c0ee4..50c62ef9184 100644 --- a/classes/controllers/grid/GridHandler.php +++ b/classes/controllers/grid/GridHandler.php @@ -40,6 +40,7 @@ namespace PKP\controllers\grid; use APP\template\TemplateManager; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Enumerable; use Illuminate\Support\LazyCollection; use Illuminate\Support\Str; @@ -47,6 +48,7 @@ use PKP\core\ItemIterator; use PKP\core\JSONMessage; use PKP\core\PKPRequest; +use PKP\db\DAOResultFactory; use PKP\db\DBResultRange; use PKP\form\Form; use PKP\handler\PKPHandler; @@ -398,17 +400,12 @@ public function setGridDataElements($data) { $this->callFeaturesHook('setGridDataElements', ['grid' => &$this, 'data' => &$data]); - if ($data instanceof Enumerable) { - $this->_data = $this->toAssociativeArray($data); - } elseif (is_iterable($data)) { - $this->_data = $data; - } elseif ($data instanceof \PKP\db\DAOResultFactory) { - $this->_data = $data->toAssociativeArray(); - } elseif ($data instanceof ItemIterator) { - $this->_data = $data->toArray(); - } else { - assert(false); - } + $this->_data = match (true) { + $data instanceof Enumerable => $this->toAssociativeArray($data), + $data instanceof DAOResultFactory => $data->toAssociativeArray(), + $data instanceof ItemIterator => $data->toArray(), + is_iterable($data) => $data + }; } /** @@ -1378,7 +1375,11 @@ public static function toAssociativeArray(LazyCollection $lazyCollection, string { $returner = []; foreach ($lazyCollection as $item) { - $returner[$item->getData($idField)] = $item; + if ($item instanceof Model) { + $returner[$item->$idField] = $item; + } else { + $returner[$item->getData($idField)] = $item; + } } return $returner; } diff --git a/classes/core/PKPApplication.php b/classes/core/PKPApplication.php index 65f8f071997..47b9ac1d545 100644 --- a/classes/core/PKPApplication.php +++ b/classes/core/PKPApplication.php @@ -3,8 +3,8 @@ /** * @file classes/core/PKPApplication.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class PKPApplication @@ -487,7 +487,6 @@ public function getDAOMap(): array 'SubmissionCommentDAO' => 'PKP\submission\SubmissionCommentDAO', 'SubmissionDisciplineDAO' => 'PKP\submission\SubmissionDisciplineDAO', 'SubmissionDisciplineEntryDAO' => 'PKP\submission\SubmissionDisciplineEntryDAO', - 'QueryDAO' => 'PKP\query\QueryDAO', 'SubmissionKeywordDAO' => 'PKP\submission\SubmissionKeywordDAO', 'SubmissionKeywordEntryDAO' => 'PKP\submission\SubmissionKeywordEntryDAO', 'SubmissionSubjectDAO' => 'PKP\submission\SubmissionSubjectDAO', diff --git a/classes/decision/types/traits/IsRecommendation.php b/classes/decision/types/traits/IsRecommendation.php index 30fd7de48ca..72035e2e27a 100644 --- a/classes/decision/types/traits/IsRecommendation.php +++ b/classes/decision/types/traits/IsRecommendation.php @@ -2,8 +2,8 @@ /** * @file classes/decision/types/traits/IsRecommendation.php * - * Copyright (c) 2014-2022 Simon Fraser University - * Copyright (c) 2000-2022 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class decision @@ -31,7 +31,7 @@ use PKP\mail\Mailable; use PKP\mail\mailables\RecommendationNotifyEditors; use PKP\note\Note; -use PKP\query\QueryDAO; +use PKP\query\Query; use PKP\security\Role; use PKP\stageAssignment\StageAssignment; use PKP\submission\reviewRound\ReviewRound; @@ -132,9 +132,7 @@ protected function addRecommendationQuery(EmailData $email, Submission $submissi } } - /** @var QueryDAO $queryDao */ - $queryDao = DAORegistry::getDAO('QueryDAO'); - $queryId = $queryDao->addQuery( + $queryId = Repo::query()->addQuery( $submission->getId(), $this->getStageId(), $email->subject, @@ -145,8 +143,8 @@ protected function addRecommendationQuery(EmailData $email, Submission $submissi false ); - $query = $queryDao->getById($queryId); - $note = $query->getHeadNote(); + $query = Query::find($queryId); + $note = Repo::note()->getHeadNote($query->id); $mailable = new Mailable(); foreach ($email->attachments as $attachment) { if (isset($attachment[Mailable::ATTACHMENT_TEMPORARY_FILE])) { diff --git a/classes/facades/Repo.php b/classes/facades/Repo.php index cbfb448f7d1..70ae1392cb7 100644 --- a/classes/facades/Repo.php +++ b/classes/facades/Repo.php @@ -3,8 +3,8 @@ /** * @file classes/facades/Repo.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class Repo @@ -38,7 +38,8 @@ use PKP\log\event\Repository as EventLogRepository; use PKP\log\Repository as EmailLogEntryRepository; use PKP\note\Repository as NoteRepository; -use PKP\notification\Notification as NotificationRepository; +use PKP\notification\Repository as NotificationRepository; +use PKP\query\Repository as QueryRepository; use PKP\stageAssignment\Repository as StageAssignmentRepository; use PKP\submissionFile\Repository as SubmissionFileRepository; use PKP\userGroup\Repository as UserGroupRepository; @@ -134,4 +135,9 @@ public static function note(): NoteRepository { return app(NoteRepository::class); } + + public static function query(): QueryRepository + { + return app(QueryRepository::class); + } } diff --git a/classes/note/Note.php b/classes/note/Note.php index 092fad325e1..2f8cc726a25 100644 --- a/classes/note/Note.php +++ b/classes/note/Note.php @@ -101,7 +101,7 @@ public function getData(?string $field) */ public function scopeWithUserId(Builder $query, int $userId): Builder { - return $query->where('userId', $userId); + return $query->where('user_id', $userId); } /** diff --git a/classes/note/Repository.php b/classes/note/Repository.php index 78fa1ef6b7e..68857620ac8 100644 --- a/classes/note/Repository.php +++ b/classes/note/Repository.php @@ -16,27 +16,24 @@ namespace PKP\note; +use PKP\db\DAO; +use PKPApplication; + class Repository { - /** - * Fetch a note by symbolic info, building it if needed. - */ - public function build(int $assocType, int $assocId, int $userId, ?string $contents, ?string $title): Note - { - return Note::withUserId($userId) - ->withAssoc($assocType, $assocId) - ->firstOr(fn() => Note::create([ - 'assocType' => $assocType, - 'assocId' => $assocId, - 'userId' => $userId, - 'contents' => $contents, - 'title' => $title, - ])); - } - public function transfer(int $oldUserId, int $newUserId): int { return Note::withUserId($oldUserId) ->update(['user_id' => $newUserId]); } + + /** + * Get the "head" (first) note for a Query. + */ + public function getHeadNote(int $queryId): ?Note + { + return Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $queryId) + ->withSort(Note::NOTE_ORDER_DATE_CREATED, DAO::SORT_DIRECTION_ASC) + ->first(); + } } diff --git a/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.php b/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.php index f172a0b0877..c84de4a6fb2 100644 --- a/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.php +++ b/classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.php @@ -3,15 +3,15 @@ /** * @file classes/notification/managerDelegate/PKPEditingProductionStatusNotificationManager.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2003-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2003-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class PKPEditingProductionStatusNotificationManager * - * @ingroup classses_notification_managerDelegate + * @ingroup classes_notification_managerDelegate * - * @brief Editing and productionstatus notifications types manager delegate. + * @brief Editing and production status notifications types manager delegate. */ namespace PKP\notification\managerDelegate; @@ -21,9 +21,9 @@ use APP\notification\NotificationManager; use PKP\core\PKPApplication; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; use PKP\notification\Notification; use PKP\notification\NotificationManagerDelegate; +use PKP\query\Query; use PKP\security\Role; use PKP\stageAssignment\StageAssignment; use PKP\submissionFile\SubmissionFile; @@ -35,7 +35,7 @@ class PKPEditingProductionStatusNotificationManager extends NotificationManagerD */ public function getNotificationMessage(PKPRequest $request, Notification $notification): string|array|null { - return match($notification->type) { + return match ($notification->type) { Notification::NOTIFICATION_TYPE_ASSIGN_COPYEDITOR => __('notification.type.assignCopyeditors'), Notification::NOTIFICATION_TYPE_AWAITING_COPYEDITS => __('notification.type.awaitingCopyedits'), Notification::NOTIFICATION_TYPE_ASSIGN_PRODUCTIONUSER => __('notification.type.assignProductionUser'), @@ -91,10 +91,10 @@ public function updateNotification(PKPRequest $request, ?array $userIds, int $as ->withRoleIds([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SUB_EDITOR]) ->get(); - // Get the copyediting and production discussions - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var \PKP\query\QueryDAO $queryDao */ - $productionQueries = $queryDao->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId, WORKFLOW_STAGE_ID_PRODUCTION); - $productionQuery = $productionQueries->next(); + // Get the production discussions + $productionQuery = Query::withAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId) + ->withStageId(WORKFLOW_STAGE_ID_PRODUCTION) + ->first(); // Get the copyedited files $countCopyeditedFiles = Repo::submissionFile() @@ -165,8 +165,10 @@ public function updateNotification(PKPRequest $request, ?array $userIds, int $as $this->_removeNotification($submissionId, $editorStageAssignment->userId, $notificationType, $contextId); } else { // If a copyeditor is assigned i.e. there is a copyediting discussion - $editingQueries = $queryDao->getByAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId, WORKFLOW_STAGE_ID_EDITING); - if ($editingQueries->next()) { + $editingQueries = Query::withAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId) + ->withStageId(WORKFLOW_STAGE_ID_EDITING) + ->first(); + if ($editingQueries) { if ($notificationType == Notification::NOTIFICATION_TYPE_AWAITING_COPYEDITS) { // Add 'awaiting copyedits' notification $this->_createNotification( @@ -239,7 +241,3 @@ public function _createNotification(PKPRequest $request, int $submissionId, int } } } - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\notification\managerDelegate\PKPEditingProductionStatusNotificationManager', '\PKPEditingProductionStatusNotificationManager'); -} diff --git a/classes/notification/managerDelegate/QueryNotificationManager.php b/classes/notification/managerDelegate/QueryNotificationManager.php index 87e7e0e4082..0d1d8dbebae 100644 --- a/classes/notification/managerDelegate/QueryNotificationManager.php +++ b/classes/notification/managerDelegate/QueryNotificationManager.php @@ -23,13 +23,10 @@ use PKP\core\PKPApplication; use PKP\core\PKPRequest; use PKP\core\PKPString; -use PKP\db\DAO; -use PKP\db\DAORegistry; use PKP\note\Note; use PKP\notification\Notification; use PKP\notification\NotificationManagerDelegate; use PKP\query\Query; -use PKP\query\QueryDAO; class QueryNotificationManager extends NotificationManagerDelegate { @@ -41,10 +38,9 @@ public function getNotificationMessage(PKPRequest $request, Notification $notifi if ($notification->assocType != Application::ASSOC_TYPE_QUERY) { throw new \Exception('Unexpected assoc type!'); } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($notification->assocId); + $query = Query::find($notification->assocId); - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); if (!$headNote) { throw new \Exception('Unable to retrieve head note for query!'); } @@ -58,14 +54,14 @@ public function getNotificationMessage(PKPRequest $request, Notification $notifi 'noteTitle' => Str::limit($headNote->title, 200), ]); case Notification::NOTIFICATION_TYPE_QUERY_ACTIVITY: - $latestNote = Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->getAssocId()) + $latestNote = Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->id) ->withSort(Note::NOTE_ORDER_ID) ->first(); $user = $latestNote->user; return __('submission.query.activity', [ 'responderName' => $user->getFullName(), 'noteContents' => Str::limit(PKPString::html2text($latestNote->contents), 200), - 'noteTitle' => Str::limit($headNote->title,200), + 'noteTitle' => Str::limit($headNote->title, 200), ]); } throw new \Exception('Unexpected notification type!'); @@ -76,12 +72,12 @@ public function getNotificationMessage(PKPRequest $request, Notification $notifi */ protected function getQuerySubmission(Query $query): Submission { - switch ($query->getAssocType()) { + switch ($query->assocType) { case Application::ASSOC_TYPE_SUBMISSION: - return Repo::submission()->get($query->getAssocId()); + return Repo::submission()->get($query->assocId); case Application::ASSOC_TYPE_REPRESENTATION: $representationDao = Application::getRepresentationDAO(); - $representation = $representationDao->getById($query->getAssocId()); + $representation = $representationDao->getById($query->assocId); $publication = Repo::publication()->get($representation->getData('publicationId')); return Repo::submission()->get($publication->getData('submissionId')); } @@ -97,8 +93,7 @@ public function getNotificationUrl(PKPRequest $request, Notification $notificati throw new \Exception('Unexpected query assoc type!'); } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($notification->assocId); + $query = Query::find($notification->assocId); if (!$query) { return null; } @@ -112,11 +107,10 @@ public function getNotificationUrl(PKPRequest $request, Notification $notificati */ public function getNotificationContents(PKPRequest $request, Notification $notification): mixed { - if($notification->assocType != Application::ASSOC_TYPE_QUERY) { + if ($notification->assocType != Application::ASSOC_TYPE_QUERY) { throw new \Exception('Unexpected assoc type!'); } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($notification->assocId); + $query = Query::find($notification->assocId); $submission = $this->getQuerySubmission($query); switch ($notification->type) { @@ -124,7 +118,7 @@ public function getNotificationContents(PKPRequest $request, Notification $notif return __( 'submission.query.new.contents', [ - 'queryTitle' => $query->getHeadNote()->title, + 'queryTitle' => Repo::note()->getHeadNote($query->id)->title, 'submissionTitle' => $submission->getCurrentPublication()->getLocalizedTitle(null, 'html'), ] ); @@ -132,7 +126,7 @@ public function getNotificationContents(PKPRequest $request, Notification $notif return __( 'submission.query.activity.contents', [ - 'queryTitle' => $query->getHeadNote()->title, + 'queryTitle' => Repo::note()->getHeadNote($query->id)->title, 'submissionTitle' => $submission->getCurrentPublication()->getLocalizedTitle(null, 'html'), ] ); @@ -148,7 +142,3 @@ public function getStyleClass(Notification $notification): string return NOTIFICATION_STYLE_CLASS_WARNING; } } - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\notification\managerDelegate\QueryNotificationManager', '\QueryNotificationManager'); -} diff --git a/classes/query/Query.php b/classes/query/Query.php index aa1d8e20664..8997b8d9259 100644 --- a/classes/query/Query.php +++ b/classes/query/Query.php @@ -3,142 +3,131 @@ /** * @file classes/query/Query.php * - * Copyright (c) 2016-2024 Simon Fraser University - * Copyright (c) 2003-2024 John Willinsky + * Copyright (c) 2024 Simon Fraser University + * Copyright (c) 2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class Query * - * @ingroup submission - * - * @see QueryDAO - * * @brief Class for Query. */ namespace PKP\query; -use Illuminate\Support\LazyCollection; -use PKP\core\PKPApplication; -use PKP\db\DAORegistry; -use PKP\db\DAO; -use PKP\note\Note; +use APP\facades\Repo; +use Eloquence\Behaviours\HasCamelCasing; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; -class Query extends \PKP\core\DataObject +class Query extends Model { - /** - * Get query assoc type - * - * @return int Application::ASSOC_TYPE_... - */ - public function getAssocType() - { - return $this->getData('assocType'); - } + use HasCamelCasing; - /** - * Set query assoc type - * - * @param int $assocType Application::ASSOC_TYPE_... - */ - public function setAssocType($assocType) - { - $this->setData('assocType', $assocType); - } + const CREATED_AT = 'date_posted'; + const UPDATED_AT = 'date_modified'; - /** - * Get query assoc ID - * - * @return int - */ - public function getAssocId() + protected $table = 'queries'; + protected $primaryKey = 'query_id'; + + protected $fillable = [ + 'assocType', 'assocId', 'stageId', 'seq', + 'datePosted', 'dateModified', 'closed' + ]; + + protected function casts(): array { - return $this->getData('assocId'); + return [ + 'assocType' => 'int', + 'assocId' => 'int', + 'stageId' => 'int', + 'seq' => 'float', + 'datePosted' => 'datetime', + 'dateModified' => 'datetime', + 'closed' => 'boolean' + ]; } /** - * Set query assoc ID - * - * @param int $assocId + * Accessor and Mutator for primary key => id */ - public function setAssocId($assocId) + protected function id(): Attribute { - $this->setData('assocId', $assocId); + return Attribute::make( + get: fn($value, $attributes) => $attributes[$this->primaryKey] ?? null, + set: fn($value) => [$this->primaryKey => $value], + ); } /** - * Get stage ID - * - * @return int + * Accessor for users. Can be replaced with relationship once User is converted to an Eloquent Model. */ - public function getStageId() + protected function users(): Attribute { - return $this->getData('stageId'); + return Attribute::make( + get: function () { + $userIds = $this->queryParticipants() + ->pluck('user_id') + ->all(); + return Repo::user()->getCollector()->filterByUserIds($userIds)->getMany(); + }, + ); } /** - * Set stage ID - * - * @param int $stageId + * Relationship to Query Participants. Can be replaced with Many-to-Many relationship once + * User is converted to an Eloquent Model. */ - public function setStageId($stageId) + public function queryParticipants(): HasMany { - return $this->setData('stageId', $stageId); + return $this->hasMany(QueryParticipant::class, 'query_id', 'query_id'); } + // Scopes + /** - * Get sequence of query. - * - * @return float + * Scope a query to only include queries with a specific assoc type and assoc ID. */ - public function getSequence() + public function scopeWithAssoc(Builder $query, int $assocType, int $assocId): Builder { - return $this->getData('sequence'); + return $query->where('assoc_type', $assocType) + ->where('assoc_id', $assocId); } /** - * Set sequence of query. - * - * @param float $sequence + * Scope a query to only include queries with a specific stage ID. */ - public function setSequence($sequence) + public function scopeWithStageId(Builder $query, int $stageId): Builder { - $this->setData('sequence', $sequence); + return $query->where('stage_id', $stageId); } /** - * Get closed flag - * - * @return bool + * Scope a query to only include queries with a specific closed status. */ - public function getIsClosed() + public function scopeWithClosed(Builder $query, bool $closed): Builder { - return $this->getData('closed'); + return $query->where('closed', $closed); } /** - * Set closed flag - * - * @param bool $isClosed + * Scope a query to only include queries with a specific user ID. */ - public function setIsClosed($isClosed) + public function scopeWithUserId(Builder $query, int $userId): Builder { - return $this->setData('closed', $isClosed); + return $query->whereHas('queryParticipants', function ($q) use ($userId) { + $q->where('user_id', $userId); + }); } /** - * Get the "head" (first) note for this query. - * - * @return Note + * Scope a query to only include queries with specific user IDs. */ - public function getHeadNote() + public function scopeWithUserIds($query, array $userIds) { - return Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $this->getId()) - ->withSort(Note::NOTE_ORDER_DATE_CREATED, DAO::SORT_DIRECTION_ASC) - ->first(); + return $query->whereHas('queryParticipants', function ($q) use ($userIds) { + $q->whereIn('user_id', $userIds); + }); } } - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\query\Query', '\Query'); -} diff --git a/classes/query/QueryDAO.php b/classes/query/QueryDAO.php deleted file mode 100644 index e1734f520ce..00000000000 --- a/classes/query/QueryDAO.php +++ /dev/null @@ -1,490 +0,0 @@ -retrieve( - 'SELECT * - FROM queries - WHERE query_id = ?' - . ($assocType ? ' AND assoc_type = ? AND assoc_id = ?' : ''), - $params - ); - $row = $result->current(); - return $row ? $this->_fromRow((array) $row) : null; - } - - /** - * Retrieve all queries by association - * - * @param int $assocType Application::ASSOC_TYPE_... - * @param int $assocId Assoc ID - * @param int $stageId Optional stage ID - * @param int $userId Optional user ID; when set, show only assigned queries - * - * @return DAOResultFactory Query - */ - public function getByAssoc($assocType, $assocId, $stageId = null, $userId = null) - { - $params = []; - $params[] = (int) Application::ASSOC_TYPE_QUERY; - if ($userId) { - $params[] = (int) $userId; - } - $params[] = (int) $assocType; - $params[] = (int) $assocId; - if ($stageId) { - $params[] = (int) $stageId; - } - if ($userId) { - $params[] = (int) $userId; - } - - return new DAOResultFactory( - $this->retrieve( - 'SELECT DISTINCT q.* - FROM queries q - LEFT JOIN notes n ON n.assoc_type = ? AND n.assoc_id = q.query_id - ' . ($userId ? 'INNER JOIN query_participants qp ON (q.query_id = qp.query_id AND qp.user_id = ?)' : '') . ' - WHERE q.assoc_type = ? AND q.assoc_id = ? - ' . ($stageId ? ' AND q.stage_id = ?' : '') . - ($userId ? ' - AND (n.user_id = ? OR n.title IS NOT NULL - OR n.contents IS NOT NULL)' : '') . ' - ORDER BY q.seq', - $params - ), - $this, - '_fromRow' - ); - } - - /** - * Retrieve a count of all open queries totalled by stage - * - * @param int[] $participantIds Only include queries with these participants - * - * @return array [int $stageId => int $count] - */ - public function countOpenPerStage(int $submissionId, ?array $participantIds = null) - { - $counts = DB::table('queries as q') - ->when($participantIds !== null, function (Builder $q) use ($participantIds) { - $q->join('query_participants as qp', 'q.query_id', '=', 'qp.query_id') - ->whereIn('qp.user_id', $participantIds); - }) - ->where('q.assoc_type', '=', Application::ASSOC_TYPE_SUBMISSION) - ->where('q.assoc_id', '=', $submissionId) - ->where('q.closed', '=', 0) - ->select(['q.stage_id', DB::raw('COUNT(q.stage_id) as count')]) - ->groupBy(['q.stage_id']) - ->get() - ->mapWithKeys(fn ($row, $key) => [$row->stage_id => $row->count]) - ->toArray(); - - return collect(Application::get()->getApplicationStages()) - ->mapWithKeys(fn ($stageId, $key) => [$stageId => $counts[$stageId] ?? 0]); - } - - /** - * Internal function to return a submission query object from a row. - * - * @param array $row - * - * @return Query - * - * @hook QueryDAO::_fromRow [[&$query, &$row]] - */ - public function _fromRow($row) - { - $query = $this->newDataObject(); - $query->setId($row['query_id']); - $query->setAssocType($row['assoc_type']); - $query->setAssocId($row['assoc_id']); - $query->setStageId($row['stage_id']); - $query->setIsClosed($row['closed']); - $query->setSequence($row['seq']); - - Hook::call('QueryDAO::_fromRow', [&$query, &$row]); - return $query; - } - - /** - * Get a new data object - * - * @return Query - */ - public function newDataObject() - { - return new Query(); - } - - /** - * Insert a new Query. - * - * @param Query $query - * - * @return int New query ID - */ - public function insertObject($query) - { - $this->update( - 'INSERT INTO queries (assoc_type, assoc_id, stage_id, closed, seq) - VALUES (?, ?, ?, ?, ?)', - [ - (int) $query->getAssocType(), - (int) $query->getAssocId(), - (int) $query->getStageId(), - (int) $query->getIsClosed(), - (float) $query->getSequence(), - ] - ); - $query->setId($this->getInsertId()); - return $query->getId(); - } - - /** - * Adds a participant to a query. - * - * @param int $queryId Query ID - * @param int $userId User ID - */ - public function insertParticipant($queryId, $userId) - { - $this->update( - 'INSERT INTO query_participants - (query_id, user_id) - VALUES - (?, ?)', - [(int) $queryId, (int) $userId] - ); - } - - /** - * Removes a participant from a query. - * - * @param int $queryId Query ID - * @param int $userId User ID - */ - public function removeParticipant($queryId, $userId) - { - $this->update( - 'DELETE FROM query_participants WHERE query_id = ? AND user_id = ?', - [(int) $queryId, (int) $userId] - ); - } - - /** - * Removes all participants from a query. - * - * @param int $queryId Query ID - */ - public function removeAllParticipants($queryId) - { - $this->update( - 'DELETE FROM query_participants WHERE query_id = ?', - [(int) $queryId] - ); - } - - /** - * Retrieve all participant user IDs for a query. - * - * @param int $queryId Query ID - * @param int $userId User ID to restrict results to - * - * @return array - */ - public function getParticipantIds($queryId, $userId = null) - { - $params = [(int) $queryId]; - if ($userId) { - $params[] = (int) $userId; - } - $result = $this->retrieve( - 'SELECT user_id - FROM query_participants - WHERE query_id = ?' . - ($userId ? ' AND user_id = ?' : ''), - $params - ); - $userIds = []; - foreach ($result as $row) { - $userIds[] = (int) $row->user_id; - } - return $userIds; - } - - /** - * Update an existing Query. - * - * @param Query $query - */ - public function updateObject($query) - { - $this->update( - 'UPDATE queries - SET assoc_type = ?, - assoc_id = ?, - stage_id = ?, - closed = ?, - seq = ? - WHERE query_id = ?', - [ - (int) $query->getAssocType(), - (int) $query->getAssocId(), - (int) $query->getStageId(), - (int) $query->getIsClosed(), - (float) $query->getSequence(), - (int) $query->getId() - ] - ); - } - - /** - * Delete a submission query. - * - * @param Query $query - */ - public function deleteObject($query) - { - $this->deleteById($query->getId()); - } - - /** - * Delete a submission query by ID. - * - * Deletes any associated notes and notifications. The - * participants will be deleted automatically through - * the onDelete CASCADE foreign key relationship in - * the database table. - * - * @param $queryId Query ID - * @param $assocType Optional Application::ASSOC_TYPE_... - * @param $assocId Optional assoc ID per assocType - */ - public function deleteById(int $queryId, ?int $assocType = null, ?int $assocId = null): int - { - $countDeleted = DB::table('queries') - ->where('query_id', '=', $queryId) - ->when(!is_null($assocType), function (Builder $q) use ($assocType) { - $q->where('assoc_type', '=', $assocType); - }) - ->when(!is_null($assocId), function (Builder $q) use ($assocId) { - $q->where('assoc_id', '=', $assocId); - }) - ->delete(); - - if ($countDeleted) { - Note::withAssoc(Application::ASSOC_TYPE_QUERY, $queryId)->delete(); - Notification::withAssoc(Application::ASSOC_TYPE_QUERY, $queryId)->delete(); - } - - return $countDeleted; - } - - /** - * Sequentially renumber queries in their sequence order. - * - * @param int $assocType Application::ASSOC_TYPE_... - * @param int $assocId Assoc ID per assocType - */ - public function resequence($assocType, $assocId) - { - $result = $this->retrieve( - 'SELECT query_id FROM queries WHERE assoc_type = ? AND assoc_id = ? ORDER BY seq', - [(int) $assocType, (int) $assocId] - ); - - for ($i = 1; $row = $result->current(); $i++) { - $this->update('UPDATE queries SET seq = ? WHERE query_id = ?', [$i, $row->query_id]); - $result->next(); - } - } - - /** - * Delete queries by assoc info. - * - * @param int $assocType Application::ASSOC_TYPE_... - * @param int $assocId Assoc ID per assocType - */ - public function deleteByAssoc($assocType, $assocId) - { - $queries = $this->getByAssoc($assocType, $assocId); - while ($query = $queries->next()) { - $this->deleteObject($query); - } - } - - /** - * Start a query - * - * Inserts the query, assigns participants, and creates the head note - * - * @return int The new query id - */ - public function addQuery(int $submissionId, int $stageId, string $title, string $content, User $fromUser, array $participantUserIds, int $contextId, bool $sendEmail = true): int - { - $query = $this->newDataObject(); - $query->setAssocType(Application::ASSOC_TYPE_SUBMISSION); - $query->setAssocId($submissionId); - $query->setStageId($stageId); - $query->setSequence(REALLY_BIG_NUMBER); - $this->insertObject($query); - $this->resequence(Application::ASSOC_TYPE_SUBMISSION, $submissionId); - - foreach (array_unique($participantUserIds) as $participantUserId) { - $this->insertParticipant($query->getId(), $participantUserId); - } - - $note = Note::create([ - 'assocType' => Application::ASSOC_TYPE_QUERY, - 'assocId' => $query->getId(), - 'contents' => $content, - 'title' => $title, - 'userId' => $fromUser->getId(), - ]); - - // Add task for assigned participants - $notificationMgr = new NotificationManager(); - - /** @var NotificationSubscriptionSettingsDAO */ - $notificationSubscriptionSettingsDao = DAORegistry::getDAO('NotificationSubscriptionSettingsDAO'); - - foreach ($participantUserIds as $participantUserId) { - $notificationMgr->createNotification( - Application::get()->getRequest(), - $participantUserId, - Notification::NOTIFICATION_TYPE_NEW_QUERY, - $contextId, - Application::ASSOC_TYPE_QUERY, - $query->getId(), - Notification::NOTIFICATION_LEVEL_TASK - ); - - if (!$sendEmail) { - continue; - } - - // Check if the user is unsubscribed - $notificationSubscriptionSettings = $notificationSubscriptionSettingsDao->getNotificationSubscriptionSettings( - NotificationSubscriptionSettingsDAO::BLOCKED_EMAIL_NOTIFICATION_KEY, - $participantUserId, - $contextId - ); - if (in_array(Notification::NOTIFICATION_TYPE_NEW_QUERY, $notificationSubscriptionSettings)) { - continue; - } - - $recipient = Repo::user()->get($participantUserId); - $mailable = new Mailable(); - $mailable->to($recipient->getEmail(), $recipient->getFullName()); - $mailable->from($fromUser->getEmail(), $fromUser->getFullName()); - $mailable->subject($title); - $mailable->body($content); - - Mail::send($mailable); - } - - return $query->getId(); - } - - /** - * Create a query with a submission's comments for the editors - * - * Creates the query and assigns all participants - * - * @return int new query id - */ - public function addCommentsForEditorsQuery(Submission $submission): int - { - // Replaces StageAssignmentDAO::getBySubmissionAndRoleIds - $participantUserIds = StageAssignment::withSubmissionIds([$submission->getId()]) - ->withRoleIds([ - Role::ROLE_ID_MANAGER, - Role::ROLE_ID_SUB_EDITOR, - Role::ROLE_ID_ASSISTANT, - Role::ROLE_ID_AUTHOR, - ]) - ->withStageIds([$submission->getData('stageId')]) - ->get() - ->pluck('userId') - ->all(); - - // Replaces StageAssignmentDAO::getBySubmissionAndRoleIds - $authorAssignments = StageAssignment::withSubmissionIds([$submission->getId()]) - ->withRoleIds([Role::ROLE_ID_AUTHOR]) - ->withStageIds([$submission->getData('stageId')]) - ->get(); - - $fromUser = $authorAssignments->isEmpty() - ? Application::get()->getRequest()->getUser() - : Repo::user()->get($authorAssignments->first()->userId); - - return $this->addQuery( - $submission->getId(), - $submission->getData('stageId'), - __('submission.submit.coverNote'), - $submission->getData('commentsForTheEditors'), - $fromUser, - $participantUserIds, - $submission->getData('contextId') - ); - } -} - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\query\QueryDAO', '\QueryDAO'); -} diff --git a/classes/query/QueryParticipant.php b/classes/query/QueryParticipant.php new file mode 100644 index 00000000000..8f09dcf8d8d --- /dev/null +++ b/classes/query/QueryParticipant.php @@ -0,0 +1,88 @@ + 'int', + 'userId' => 'int' + ]; + } + public function toQuery(): BelongsTo + { + return $this->belongsTo(Query::class, 'query_id', 'query_id'); + } + + /** + * Accessor and Mutator for primary key => id + */ + protected function id(): Attribute + { + return Attribute::make( + get: fn($value, $attributes) => $attributes[$this->primaryKey] ?? null, + set: fn($value) => [$this->primaryKey => $value], + ); + } + + /** + * Accessor for user. Can be replaced with relationship once User is converted to an Eloquent Model. + */ + protected function user(): Attribute + { + return Attribute::make( + get: function () { + return Repo::user()->get($this->userId, true); + }, + ); + } + + // Scopes + + /** + * Scope a query to only include query participants with a specific query ID. + */ + public function scopeWithQueryId(Builder $query, int $queryId): Builder + { + return $query->where('query_id', $queryId); + } + + /** + * Scope a query to only include query participants with a specific user ID. + */ + public function scopeWithUserId(Builder $query, int $userId): Builder + { + return $query->where('user_id', $userId); + } +} diff --git a/classes/query/Repository.php b/classes/query/Repository.php new file mode 100644 index 00000000000..8713aa60e4a --- /dev/null +++ b/classes/query/Repository.php @@ -0,0 +1,199 @@ + [int $stageId => int $count] + */ + public function countOpenPerStage(int $submissionId, ?array $participantIds = null): array + { + $counts = Query::withAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId) + ->when($participantIds !== null, function ($q) use ($participantIds) { + $q->withUserIds($participantIds); + }) + ->withClosed(false) + ->selectRaw('stage_id, COUNT(stage_id) as count') + ->groupBy('stage_id') + ->get() + ->mapWithKeys(fn ($row, $key) => [$row->stage_id => $row->count]) + ->toArray(); + + return collect(Application::get()->getApplicationStages()) + ->mapWithKeys(fn ($stageId, $key) => [$stageId => $counts[$stageId] ?? 0]) + ->toArray(); + } + + /** + * Sequentially renumber queries in their sequence order. + * + * @param int $assocType Application::ASSOC_TYPE_... + * @param int $assocId Assoc ID per assocType + */ + public function resequence($assocType, $assocId): void + { + $result = Query::withAssoc($assocType, $assocId) + ->orderBy('seq') + ->get(); + + $result->each(function (Query $item, int $key = 1) { + $item->update(['seq' => $key]); + }); + } + + /** + * Start a query + * + * Inserts the query, assigns participants, and creates the head note + * + * @return int The new query id + */ + public function addQuery(int $submissionId, int $stageId, string $title, string $content, User $fromUser, array $participantUserIds, int $contextId, bool $sendEmail = true): int + { + $maxSeq = Query::withAssoc(Application::ASSOC_TYPE_SUBMISSION, $submissionId) + ->max('seq') ?? 0; + + $query = Query::create([ + 'assocType' => Application::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submissionId, + 'stageId' => $stageId, + 'seq' => $maxSeq + 1 + ]); + + $addParticipants = []; + foreach (array_unique($participantUserIds) as $participantUserId) { + $addParticipants[] = ([ + 'query_id' => $query->id, + 'user_id' => $participantUserId + ]); + } + QueryParticipant::insert($addParticipants); + + Note::create([ + 'assocType' => Application::ASSOC_TYPE_QUERY, + 'assocId' => $query->id, + 'title' => $title, + 'contents' => $content, + 'userId' => $fromUser->getId(), + ]); + + // Add task for assigned participants + $notificationMgr = new NotificationManager(); + + /** @var NotificationSubscriptionSettingsDAO */ + $notificationSubscriptionSettingsDao = DAORegistry::getDAO('NotificationSubscriptionSettingsDAO'); + + foreach ($participantUserIds as $participantUserId) { + $notificationMgr->createNotification( + Application::get()->getRequest(), + $participantUserId, + Notification::NOTIFICATION_TYPE_NEW_QUERY, + $contextId, + Application::ASSOC_TYPE_QUERY, + $query->id, + Notification::NOTIFICATION_LEVEL_TASK + ); + + if (!$sendEmail) { + continue; + } + + // Check if the user is unsubscribed + $notificationSubscriptionSettings = $notificationSubscriptionSettingsDao->getNotificationSubscriptionSettings( + NotificationSubscriptionSettingsDAO::BLOCKED_EMAIL_NOTIFICATION_KEY, + $participantUserId, + $contextId + ); + if (in_array(Notification::NOTIFICATION_TYPE_NEW_QUERY, $notificationSubscriptionSettings)) { + continue; + } + + $recipient = Repo::user()->get($participantUserId); + $mailable = new Mailable(); + $mailable->to($recipient->getEmail(), $recipient->getFullName()); + $mailable->from($fromUser->getEmail(), $fromUser->getFullName()); + $mailable->subject($title); + $mailable->body($content); + + Mail::send($mailable); + } + + return $query->id; + } + + /** + * Create a query with a submission's comments for the editors + * + * Creates the query and assigns all participants + * + * @return int new query id + */ + public function addCommentsForEditorsQuery(Submission $submission): int + { + // Replaces StageAssignmentDAO::getBySubmissionAndRoleIds + $participantUserIds = StageAssignment::withSubmissionIds([$submission->getId()]) + ->withRoleIds([ + Role::ROLE_ID_MANAGER, + Role::ROLE_ID_SUB_EDITOR, + Role::ROLE_ID_ASSISTANT, + Role::ROLE_ID_AUTHOR, + ]) + ->withStageIds([$submission->getData('stageId')]) + ->get() + ->pluck('user_id') + ->all(); + + // Replaces StageAssignmentDAO::getBySubmissionAndRoleIds + $authorAssignments = StageAssignment::withSubmissionIds([$submission->getId()]) + ->withRoleIds([Role::ROLE_ID_AUTHOR]) + ->withStageIds([$submission->getData('stageId')]) + ->get(); + + $fromUser = $authorAssignments->isEmpty() + ? Application::get()->getRequest()->getUser() + : Repo::user()->get($authorAssignments->first()->userId); + + return $this->addQuery( + $submission->getId(), + $submission->getData('stageId'), + __('submission.submit.coverNote'), + $submission->getData('commentsForTheEditors'), + $fromUser, + $participantUserIds, + $submission->getData('contextId') + ); + } +} diff --git a/classes/security/authorization/NoteAccessPolicy.php b/classes/security/authorization/NoteAccessPolicy.php index ee48386fc77..2cfe6f71493 100644 --- a/classes/security/authorization/NoteAccessPolicy.php +++ b/classes/security/authorization/NoteAccessPolicy.php @@ -2,8 +2,8 @@ /** * @file classes/security/authorization/NoteAccessPolicy.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class NoteAccessPolicy @@ -21,7 +21,6 @@ use APP\core\Application; use APP\core\Request; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; use PKP\note\Note; class NoteAccessPolicy extends AuthorizationPolicy @@ -80,12 +79,12 @@ public function effect() } // Note, query, submission and assigned stages must match - if ($note->assocId != $query->getId() + if ($note->assocId != $query->id || $note->assocType != Application::ASSOC_TYPE_QUERY - || $query->getAssocId() != $submission->getId() - || $query->getAssocType() != Application::ASSOC_TYPE_SUBMISSION - || !array_key_exists($query->getStageId(), $assignedStages) - || empty($assignedStages[$query->getStageId()])) { + || $query->assocId != $submission->getId() + || $query->assocType != Application::ASSOC_TYPE_SUBMISSION + || !array_key_exists($query->stageId, $assignedStages) + || empty($assignedStages[$query->stageId])) { return AuthorizationPolicy::AUTHORIZATION_DENY; } diff --git a/classes/security/authorization/internal/QueryAssignedToUserAccessPolicy.php b/classes/security/authorization/internal/QueryAssignedToUserAccessPolicy.php index a5b7e969f58..ad2ef8521ba 100644 --- a/classes/security/authorization/internal/QueryAssignedToUserAccessPolicy.php +++ b/classes/security/authorization/internal/QueryAssignedToUserAccessPolicy.php @@ -2,8 +2,8 @@ /** * @file classes/security/authorization/internal/QueryAssignedToUserAccessPolicy.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class QueryAssignedToUserAccessPolicy @@ -18,10 +18,11 @@ use APP\core\Application; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; -use PKP\query\QueryDAO; +use PKP\query\Query; +use PKP\query\QueryParticipant; use PKP\security\authorization\AuthorizationPolicy; use PKP\security\Role; +use PKP\user\User; class QueryAssignedToUserAccessPolicy extends AuthorizationPolicy { @@ -49,26 +50,28 @@ public function effect() { // A query should already be in the context. $query = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_QUERY); - if (!$query instanceof \PKP\query\Query) { + if (!$query instanceof Query) { return AuthorizationPolicy::AUTHORIZATION_DENY; } // Check that there is a currently logged in user. $user = $this->_request->getUser(); - if (!$user instanceof \PKP\user\User) { + if (!$user instanceof User) { return AuthorizationPolicy::AUTHORIZATION_DENY; } // Determine if the query is assigned to the user. - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - if ($queryDao->getParticipantIds($query->getId(), $user->getId())) { + $participantIds = QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(); + if (in_array($user->getId(), $participantIds)) { return AuthorizationPolicy::AUTHORIZATION_PERMIT; } // Managers are allowed to access discussions they are not participants in // as long as they have Manager-level access to the workflow stage $accessibleWorkflowStages = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_ACCESSIBLE_WORKFLOW_STAGES); - $managerAssignments = array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $accessibleWorkflowStages[$query->getStageId()] ?? []); + $managerAssignments = array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN], $accessibleWorkflowStages[$query->stageId] ?? []); if (!empty($managerAssignments)) { return AuthorizationPolicy::AUTHORIZATION_PERMIT; } @@ -77,7 +80,3 @@ public function effect() return AuthorizationPolicy::AUTHORIZATION_DENY; } } - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\security\authorization\internal\QueryAssignedToUserAccessPolicy', '\QueryAssignedToUserAccessPolicy'); -} diff --git a/classes/security/authorization/internal/QueryRequiredPolicy.php b/classes/security/authorization/internal/QueryRequiredPolicy.php index c17b31e4975..ae5f63a74c1 100644 --- a/classes/security/authorization/internal/QueryRequiredPolicy.php +++ b/classes/security/authorization/internal/QueryRequiredPolicy.php @@ -2,8 +2,8 @@ /** * @file classes/security/authorization/internal/QueryRequiredPolicy.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class QueryRequiredPolicy @@ -18,9 +18,7 @@ use APP\core\Application; use APP\submission\Submission; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; use PKP\query\Query; -use PKP\query\QueryDAO; use PKP\security\authorization\AuthorizationPolicy; use PKP\security\authorization\DataObjectRequiredPolicy; @@ -52,18 +50,17 @@ public function dataObjectEffect() } // Make sure the query belongs to the submission. - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($queryId); + $query = Query::find($queryId); if (!$query instanceof Query) { return AuthorizationPolicy::AUTHORIZATION_DENY; } - switch ($query->getAssocType()) { + switch ($query->assocType) { case Application::ASSOC_TYPE_SUBMISSION: $submission = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_SUBMISSION); if (!$submission instanceof Submission) { return AuthorizationPolicy::AUTHORIZATION_DENY; } - if ($query->getAssocId() != $submission->getId()) { + if ($query->assocId != $submission->getId()) { return AuthorizationPolicy::AUTHORIZATION_DENY; } break; @@ -76,7 +73,3 @@ public function dataObjectEffect() return AuthorizationPolicy::AUTHORIZATION_PERMIT; } } - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\security\authorization\internal\QueryRequiredPolicy', '\QueryRequiredPolicy'); -} diff --git a/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.php b/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.php index 9c6107704d2..65b7e603c71 100644 --- a/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.php +++ b/classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.php @@ -2,8 +2,8 @@ /** * @file classes/security/authorization/internal/SubmissionFileAssignedQueryAccessPolicy.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class SubmissionFileAssignedQueryAccessPolicy @@ -18,10 +18,12 @@ namespace PKP\security\authorization\internal; use APP\core\Application; -use PKP\db\DAORegistry; use PKP\note\Note; -use PKP\query\QueryDAO; +use PKP\query\Query; +use PKP\query\QueryParticipant; use PKP\security\authorization\AuthorizationPolicy; +use PKP\submissionFile\SubmissionFile; +use PKP\user\User; class SubmissionFileAssignedQueryAccessPolicy extends SubmissionFileBaseAccessPolicy { @@ -37,13 +39,13 @@ public function effect() // Get the user $user = $request->getUser(); - if (!$user instanceof \PKP\user\User) { + if (!$user instanceof User) { return AuthorizationPolicy::AUTHORIZATION_DENY; } // Get the submission file $submissionFile = $this->getSubmissionFile($request); - if (!$submissionFile instanceof \PKP\submissionFile\SubmissionFile) { + if (!$submissionFile instanceof SubmissionFile) { return AuthorizationPolicy::AUTHORIZATION_DENY; } @@ -53,27 +55,25 @@ public function effect() } $note = Note::find($submissionFile->getData('assocId')); - if (!$note instanceof \PKP\note\Note) { + if (!$note instanceof Note) { return AuthorizationPolicy::AUTHORIZATION_DENY; } if ($note->assocType != Application::ASSOC_TYPE_QUERY) { return AuthorizationPolicy::AUTHORIZATION_DENY; } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($note->assocId); + $query = Query::find($note->assocId); if (!$query) { return AuthorizationPolicy::AUTHORIZATION_DENY; } - if ($queryDao->getParticipantIds($note->assocId, $user->getId())) { + $participantIds = QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(); + if (in_array($user->getId(), $participantIds)) { return AuthorizationPolicy::AUTHORIZATION_PERMIT; } return AuthorizationPolicy::AUTHORIZATION_DENY; } } - -if (!PKP_STRICT_MODE) { - class_alias('\PKP\security\authorization\internal\SubmissionFileAssignedQueryAccessPolicy', '\SubmissionFileAssignedQueryAccessPolicy'); -} diff --git a/classes/submission/DAO.php b/classes/submission/DAO.php index ed370621dd5..0f77269aa05 100644 --- a/classes/submission/DAO.php +++ b/classes/submission/DAO.php @@ -2,8 +2,8 @@ /** * @file classes/submission/DAO.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class DAO @@ -27,7 +27,7 @@ use PKP\log\event\EventLogEntry; use PKP\note\Note; use PKP\notification\Notification; -use PKP\query\QueryDAO; +use PKP\query\Query; use PKP\services\PKPSchemaService; use PKP\stageAssignment\StageAssignment; use PKP\submission\reviewRound\ReviewRoundDAO; @@ -278,8 +278,8 @@ public function deleteById(int $id): int $reviewRoundDao->deleteBySubmissionId($id); // Delete the queries associated with a submission - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $queryDao->deleteByAssoc(Application::ASSOC_TYPE_SUBMISSION, $id); + Query::withAssoc(Application::ASSOC_TYPE_SUBMISSION, $id) + ->delete(); // Delete the stage assignments. StageAssignment::withSubmissionIds([$id]) diff --git a/classes/submission/Repository.php b/classes/submission/Repository.php index ea6e9496c99..78b5e638fac 100644 --- a/classes/submission/Repository.php +++ b/classes/submission/Repository.php @@ -2,8 +2,8 @@ /** * @file classes/submission/Repository.php * - * Copyright (c) 2014-2020 Simon Fraser University - * Copyright (c) 2000-2020 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class Repository @@ -32,7 +32,6 @@ use PKP\facades\Locale; use PKP\observers\events\SubmissionSubmitted; use PKP\plugins\Hook; -use PKP\query\QueryDAO; use PKP\security\Role; use PKP\security\RoleDAO; use PKP\services\PKPSchemaService; @@ -614,9 +613,7 @@ public function submit(Submission $submission, Context $context): void ); if ($submission->getData('commentsForTheEditors')) { - /** @var QueryDAO $queryDao */ - $queryDao = DAORegistry::getDAO('QueryDAO'); - $queryDao->addCommentsForEditorsQuery($submission); + Repo::query()->addCommentsForEditorsQuery($submission); } } diff --git a/classes/submission/maps/Schema.php b/classes/submission/maps/Schema.php index c02e101eae9..55eea5ba5b2 100644 --- a/classes/submission/maps/Schema.php +++ b/classes/submission/maps/Schema.php @@ -2,8 +2,8 @@ /** * @file classes/submission/maps/Schema.php * - * Copyright (c) 2014-2020 Simon Fraser University - * Copyright (c) 2000-2020 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class Schema @@ -22,7 +22,7 @@ use PKP\db\DAORegistry; use PKP\plugins\Hook; use PKP\plugins\PluginRegistry; -use PKP\query\QueryDAO; +use PKP\query\Query; use PKP\security\Role; use PKP\services\PKPSchemaService; use PKP\stageAssignment\StageAssignment; @@ -511,8 +511,7 @@ public function getPropertyStages(Submission $submission): array $request = Application::get()->getRequest(); $currentUser = $request->getUser(); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $openPerStage = $queryDao->countOpenPerStage($submission->getId(), [$request->getUser()->getId()]); + $openPerStage = Repo::query()->countOpenPerStage($submission->getId(), [$request->getUser()->getId()]); $stages = []; foreach ($stageIds as $stageId) { diff --git a/classes/submissionFile/Repository.php b/classes/submissionFile/Repository.php index 7a6483607b1..5d859a0afc9 100644 --- a/classes/submissionFile/Repository.php +++ b/classes/submissionFile/Repository.php @@ -2,8 +2,8 @@ /** * @file classes/submissionFile/Repository.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class Repository @@ -33,7 +33,7 @@ use PKP\note\Note; use PKP\notification\Notification; use PKP\plugins\Hook; -use PKP\query\QueryDAO; +use PKP\query\Query; use PKP\security\authorization\SubmissionFileAccessPolicy; use PKP\security\Role; use PKP\security\Validation; @@ -706,11 +706,10 @@ public function getWorkflowStageId(SubmissionFile $submissionFile): ?int } // Get the associated query. - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($note->assocId); + $query = Query::find($note->assocId); // The query will have an associated file stage. - return $query?->getStageId(); + return $query?->stageId; } throw new Exception('Could not determine the workflow stage id from submission file ' . $submissionFile->getId() . ' with file stage ' . $submissionFile->getData('fileStage')); diff --git a/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.php b/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.php index b71b034c40d..8858f5e2104 100644 --- a/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.php +++ b/controllers/grid/files/SubmissionFilesCategoryGridDataProvider.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/files/SubmissionFilesCategoryGridDataProvider.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class SubmissionFilesCategoryGridDataProvider @@ -21,7 +21,7 @@ use PKP\controllers\grid\CategoryGridDataProvider; use PKP\db\DAORegistry; use PKP\note\Note; -use PKP\query\QueryDAO; +use PKP\query\Query; use PKP\submission\reviewRound\ReviewRoundDAO; use PKP\submissionFile\SubmissionFile; @@ -149,11 +149,10 @@ public function loadCategoryData($request, $categoryDataElement, $filter = null, break; } $note = Note::find($submissionFile->getData('assocId')); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ if ($note?->assocType == Application::ASSOC_TYPE_QUERY) { - $query = $queryDao->getById($note->assocId); + $query = Query::find($note->assocId); } - if ($query && $query->getStageId() == $stageId) { + if ($query && $query->stageId == $stageId) { $stageSubmissionFiles[$key] = $submissionFile; } } diff --git a/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.php b/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.php index f914bab2fd2..6d63533958f 100644 --- a/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.php +++ b/controllers/grid/files/query/ManageQueryNoteFilesGridHandler.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/files/query/ManageQueryNoteFilesGridHandler.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2003-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2003-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class ManageQueryNoteFilesGridHandler @@ -17,6 +17,7 @@ namespace PKP\controllers\grid\files\query; use APP\core\Application; +use APP\facades\Repo; use PKP\controllers\grid\files\FilesGridCapabilities; use PKP\controllers\grid\files\query\form\ManageQueryNoteFilesForm; use PKP\controllers\grid\files\SelectableSubmissionFileListCategoryGridHandler; @@ -78,7 +79,7 @@ public function isDataElementInCategorySelected($categoryDataId, &$gridDataEleme // Passed the checks above. If it's part of the current query, mark selected. $query = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_QUERY); - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); return $submissionFile->getData('assocType') == Application::ASSOC_TYPE_NOTE && $submissionFile->getData('assocId') == $headNote->id; } @@ -98,7 +99,7 @@ public function updateQueryNoteFiles($args, $request) $submission = $this->getSubmission(); $query = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_QUERY); - $manageQueryNoteFilesForm = new ManageQueryNoteFilesForm($submission->getId(), $query->getId(), $request->getUserVar('noteId')); + $manageQueryNoteFilesForm = new ManageQueryNoteFilesForm($submission->getId(), $query->id, $request->getUserVar('noteId')); $manageQueryNoteFilesForm->readInputData(); if ($manageQueryNoteFilesForm->validate()) { diff --git a/controllers/grid/files/query/QueryNoteFilesGridDataProvider.php b/controllers/grid/files/query/QueryNoteFilesGridDataProvider.php index afcf6518028..db37f65e737 100644 --- a/controllers/grid/files/query/QueryNoteFilesGridDataProvider.php +++ b/controllers/grid/files/query/QueryNoteFilesGridDataProvider.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/files/query/QueryNoteFilesGridDataProvider.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2003-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2003-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class QueryNoteFilesGridDataProvider @@ -22,7 +22,6 @@ use PKP\controllers\api\file\linkAction\AddFileLinkAction; use PKP\controllers\grid\files\fileList\linkAction\SelectFilesLinkAction; use PKP\controllers\grid\files\SubmissionFilesGridDataProvider; -use PKP\db\DAORegistry; use PKP\note\Note; use PKP\security\authorization\QueryAccessPolicy; use PKP\submissionFile\SubmissionFile; @@ -79,7 +78,7 @@ public function loadData($filter = []) $query = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_QUERY); $note = Note::find($this->_noteId); - if ($note->assocType != Application::ASSOC_TYPE_QUERY || $note->assocId != $query->getId()) { + if ($note->assocType != Application::ASSOC_TYPE_QUERY || $note->assocId != $query->id) { throw new Exception('Invalid note ID specified!'); } @@ -107,7 +106,7 @@ public function getRequestArgs() [ 'assocType' => Application::ASSOC_TYPE_NOTE, 'assocId' => $this->_noteId, - 'queryId' => $query->getId(), + 'queryId' => $query->id, 'noteId' => $this->_noteId, 'representationId' => $representation ? $representation->getId() : null, ] @@ -132,7 +131,7 @@ public function getAddFileAction($request) null, null, false, - $query->getId() + $query->id ); } } diff --git a/controllers/grid/files/query/QueryNoteFilesGridHandler.php b/controllers/grid/files/query/QueryNoteFilesGridHandler.php index ee6ed0ac5ad..a1267f2b399 100644 --- a/controllers/grid/files/query/QueryNoteFilesGridHandler.php +++ b/controllers/grid/files/query/QueryNoteFilesGridHandler.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/files/query/QueryNoteFilesGridHandler.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2003-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2003-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class QueryNoteFilesGridHandler @@ -91,7 +91,7 @@ public function selectFiles($args, $request) $submission = $this->getSubmission(); $query = $this->getAuthorizedContextObject(Application::ASSOC_TYPE_QUERY); - $manageQueryNoteFilesForm = new ManageQueryNoteFilesForm($submission->getId(), $query->getId(), $request->getUserVar('noteId'), $this->getRequestArgs()); + $manageQueryNoteFilesForm = new ManageQueryNoteFilesForm($submission->getId(), $query->id, $request->getUserVar('noteId'), $this->getRequestArgs()); $manageQueryNoteFilesForm->initData(); return new JSONMessage(true, $manageQueryNoteFilesForm->fetch($request)); } diff --git a/controllers/grid/notifications/NotificationsGridCellProvider.php b/controllers/grid/notifications/NotificationsGridCellProvider.php index 10abe25a3cc..bbe1e393fc5 100644 --- a/controllers/grid/notifications/NotificationsGridCellProvider.php +++ b/controllers/grid/notifications/NotificationsGridCellProvider.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/notifications/NotificationsGridCellProvider.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class NotificationsGridCellProvider @@ -29,7 +29,7 @@ use PKP\linkAction\request\AjaxAction; use PKP\notification\Notification; use PKP\payment\QueuedPaymentDAO; -use PKP\query\QueryDAO; +use PKP\query\Query; use PKP\submission\reviewRound\ReviewRoundDAO; class NotificationsGridCellProvider extends GridCellProvider @@ -152,16 +152,15 @@ public function _getTitle($notification) $submissionId = $reviewRound->getSubmissionId(); break; case Application::ASSOC_TYPE_QUERY: - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($notification->assocId); + $query = Query::find($notification->assocId); assert($query instanceof \PKP\query\Query); - switch ($query->getAssocType()) { + switch ($query->assocType) { case Application::ASSOC_TYPE_SUBMISSION: - $submissionId = $query->getAssocId(); + $submissionId = $query->assocId; break; case Application::ASSOC_TYPE_REPRESENTATION: $representationDao = Application::getRepresentationDAO(); - $representation = $representationDao->getById($query->getAssocId()); + $representation = $representationDao->getById($query->assocId); $publication = Repo::publication()->get($representation->getData('publicationId')); $submissionId = $publication->getData('submissionId'); break; diff --git a/controllers/grid/queries/QueriesAccessHelper.php b/controllers/grid/queries/QueriesAccessHelper.php index d1f02b74cf5..0946fe54a17 100644 --- a/controllers/grid/queries/QueriesAccessHelper.php +++ b/controllers/grid/queries/QueriesAccessHelper.php @@ -27,9 +27,9 @@ namespace PKP\controllers\grid\queries; use APP\core\Application; -use PKP\db\DAORegistry; +use APP\facades\Repo; use PKP\query\Query; -use PKP\query\QueryDAO; +use PKP\query\QueryParticipant; use PKP\security\Role; use PKP\user\User; @@ -73,12 +73,12 @@ public function getAuthorizedContextObject($assocType) public function getCanOpenClose($query) { // Managers and sub editors are always allowed - if ($this->hasStageRole($query->getStageId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR])) { + if ($this->hasStageRole($query->stageId, [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR])) { return true; } // Assigned assistants are allowed - if ($this->hasStageRole($query->getStageId(), [Role::ROLE_ID_ASSISTANT]) && $this->isAssigned($this->_user->getId(), $query->getId())) { + if ($this->hasStageRole($query->stageId, [Role::ROLE_ID_ASSISTANT]) && $this->isAssigned($this->_user->getId(), $query->id)) { return true; } @@ -119,22 +119,21 @@ public function getCanCreate($stageId) */ public function getCanEdit($queryId) { - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($queryId); + $query = Query::find($queryId); if (!$query) { return false; } // Assistants, authors and reviewers are allowed, if they created the query less than x seconds ago - if ($this->hasStageRole($query->getStageId(), [Role::ROLE_ID_ASSISTANT, Role::ROLE_ID_AUTHOR, Role::ROLE_ID_REVIEWER])) { - $headNote = $query->getHeadNote(); + if ($this->hasStageRole($query->stageId, [Role::ROLE_ID_ASSISTANT, Role::ROLE_ID_AUTHOR, Role::ROLE_ID_REVIEWER])) { + $headNote = Repo::note()->getHeadNote($query->id); if ($headNote->userId == $this->_user->getId() && ($headNote->dateCreated->diffInHours() < 1)) { return true; } } // Managers are always allowed - if ($this->hasStageRole($query->getStageId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR])) { + if ($this->hasStageRole($query->stageId, [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, Role::ROLE_ID_SUB_EDITOR])) { return true; } @@ -152,17 +151,16 @@ public function getCanEdit($queryId) public function getCanDelete($queryId) { // Users can always delete their own placeholder queries. - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($queryId); + $query = Query::find($queryId); if ($query) { - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); if ($headNote?->userId == $this->_user->getId() && $headNote?->title == '') { return true; } } // Managers and site admins are always allowed - if ($this->hasStageRole($query->getStageId(), [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN])) { + if ($this->hasStageRole($query->stageId, [Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN])) { return true; } @@ -193,8 +191,10 @@ public function getCanListAll($stageId) */ protected function isAssigned($userId, $queryId) { - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - return (bool) $queryDao->getParticipantIds($queryId, $userId); + $participantIds = QueryParticipant::withQueryId($queryId) + ->pluck('user_id') + ->all(); + return in_array($userId, $participantIds); } /** diff --git a/controllers/grid/queries/QueriesGridCellProvider.php b/controllers/grid/queries/QueriesGridCellProvider.php index 60226aca263..71f74b21400 100644 --- a/controllers/grid/queries/QueriesGridCellProvider.php +++ b/controllers/grid/queries/QueriesGridCellProvider.php @@ -17,6 +17,7 @@ namespace PKP\controllers\grid\queries; use APP\core\Application; +use APP\facades\Repo; use APP\submission\Submission; use Illuminate\Database\Eloquent\Model; use PKP\controllers\grid\DataObjectGridCellProvider; @@ -75,9 +76,9 @@ public function getTemplateVarsFromRowColumn($row, $column) $columnId = $column->getId(); assert(($element instanceof DataObject || $element instanceof Model) && !empty($columnId)); /** @var Query $element */ - $headNote = $element->getHeadNote(); + $headNote = Repo::note()->getHeadNote($element->id); $user = $headNote?->user; - $notes = Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $element->getId()) + $notes = Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $element->id) ->withSort(Note::NOTE_ORDER_ID) ->lazy(); $context = Application::get()->getRequest()->getContext(); @@ -87,19 +88,19 @@ public function getTemplateVarsFromRowColumn($row, $column) case 'replies': return ['label' => max(0, $notes->count() - 1)]; case 'from': - return ['label' => ($user?->getUsername() ?? '—') . '
' . $headNote?->dateCreated?->locale(Locale::getLocale())?->translatedFormat($datetimeFormatShort)]; + return ['label' => ($user?->getUsername() ?? '—') . '
' . $headNote?->dateCreated->format($datetimeFormatShort)]; case 'lastReply': $latestReply = $notes->first(); - if ($latestReply && $latestReply->getId() != $headNote->id) { + if ($latestReply && $latestReply->id != $headNote->id) { $repliedUser = $latestReply->user; - return ['label' => ($repliedUser?->getUsername() ?? '—') . '
' . $latestReply->dateCreated->locale(Locale::getLocale())->translatedFormat($datetimeFormatShort)]; + return ['label' => ($repliedUser?->getUsername() ?? '—') . '
' . $latestReply->dateCreated->format($datetimeFormatShort)]; } else { return ['label' => '-']; } // no break case 'closed': return [ - 'selected' => $element->getIsClosed(), + 'selected' => $element->closed, 'disabled' => !$this->_queriesAccessHelper->getCanOpenClose($element), ]; } @@ -117,7 +118,7 @@ public function getCellActions($request, $row, $column, $position = GridHandler: switch ($column->getId()) { case 'closed': if ($this->_queriesAccessHelper->getCanOpenClose($element)) { - $enabled = !$element->getIsClosed(); + $enabled = !$element->closed; if ($enabled) { return [new LinkAction( 'close-' . $row->getId(), diff --git a/controllers/grid/queries/QueriesGridHandler.php b/controllers/grid/queries/QueriesGridHandler.php index caedd3b2524..c7c8bb55efe 100644 --- a/controllers/grid/queries/QueriesGridHandler.php +++ b/controllers/grid/queries/QueriesGridHandler.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/queries/QueriesGridHandler.php * - * Copyright (c) 2016-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2016-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class QueriesGridHandler @@ -30,15 +30,17 @@ use PKP\core\JSONMessage; use PKP\core\PKPApplication; use PKP\core\PKPRequest; +use PKP\db\DAO; use PKP\db\DAORegistry; use PKP\linkAction\LinkAction; use PKP\linkAction\request\AjaxModal; use PKP\linkAction\request\RemoteActionConfirmationModal; use PKP\log\SubmissionEmailLogEventType; +use PKP\note\Note; use PKP\notification\Notification; use PKP\notification\NotificationSubscriptionSettingsDAO; use PKP\query\Query; -use PKP\query\QueryDAO; +use PKP\query\QueryParticipant; use PKP\security\authorization\QueryAccessPolicy; use PKP\security\authorization\QueryWorkflowStageAccessPolicy; use PKP\security\Role; @@ -264,7 +266,7 @@ public function initFeatures($request, $args) */ public function getDataElementSequence($row) { - return $row->getSequence(); + return $row->seq; } /** @@ -272,10 +274,9 @@ public function getDataElementSequence($row) */ public function setDataElementSequence($request, $rowId, $gridDataElement, $newSequence) { - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($rowId, $this->getAssocType(), $this->getAssocId()); - $query->setSequence($newSequence); - $queryDao->updateObject($query); + $query = Query::where('id', $rowId)->withAssoc($this->getAssocType(), $this->getAssocId())->first(); + $query->seq = $newSequence; + $query->save(); } /** @@ -323,13 +324,12 @@ public function getRequestArgs() */ public function loadData($request, $filter = null) { - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - return $queryDao->getByAssoc( - $this->getAssocType(), - $this->getAssocId(), - $this->getStageId(), - $this->getAccessHelper()->getCanListAll($this->getStageId()) ? null : $request->getUser()->getId() - ); + $user = $this->getAccessHelper()->getCanListAll($this->getStageId()) ? null : $request->getUser()->getId(); + + return Query::withAssoc($this->getAssocType(), $this->getAssocId()) + ->withStageId($this->getStageId()) + ->when($user, fn ($q) => $q->withUserId($user)) + ->lazy(); } // @@ -370,14 +370,13 @@ public function addQuery($args, $request) public function deleteQuery($args, $request) { $query = $this->getQuery(); - if (!$request->checkCSRF() || !$query || !$this->getAccessHelper()->getCanDelete($query->getId())) { + if (!$request->checkCSRF() || !$query || !$this->getAccessHelper()->getCanDelete($query->id)) { return new JSONMessage(false); } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $queryDao->deleteObject($query); - - Notification::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->getId())->delete(); + $query->delete(); + Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->id)->delete(); + Notification::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->id)->delete(); if ($this->getStageId() == WORKFLOW_STAGE_ID_EDITING || $this->getStageId() == WORKFLOW_STAGE_ID_PRODUCTION) { @@ -397,7 +396,7 @@ public function deleteQuery($args, $request) ); } - return \PKP\db\DAO::getDataChangedEvent($query->getId()); + return DAO::getDataChangedEvent($query->id); } /** @@ -415,10 +414,9 @@ public function openQuery($args, $request) return new JSONMessage(false); } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query->setIsClosed(false); - $queryDao->updateObject($query); - return \PKP\db\DAO::getDataChangedEvent($query->getId()); + $query->closed = false; + $query->save(); + return DAO::getDataChangedEvent($query->id); } /** @@ -436,10 +434,9 @@ public function closeQuery($args, $request) return new JSONMessage(false); } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query->setIsClosed(true); - $queryDao->updateObject($query); - return \PKP\db\DAO::getDataChangedEvent($query->getId()); + $query->closed = true; + $query->save(); + return DAO::getDataChangedEvent($query->id); } /** @@ -467,10 +464,10 @@ public function readQuery($args, $request) $user = $request->getUser(); $context = $request->getContext(); - $actionArgs = array_merge($this->getRequestArgs(), ['queryId' => $query->getId()]); + $actionArgs = array_merge($this->getRequestArgs(), ['queryId' => $query->id]); // If appropriate, create an Edit action for the participants list - if ($this->getAccessHelper()->getCanEdit($query->getId())) { + if ($this->getAccessHelper()->getCanEdit($query->id)) { $editAction = new LinkAction( 'editQuery', new AjaxModal( @@ -499,7 +496,7 @@ public function readQuery($args, $request) ); // Show leave query button for journal managers included in the query - if ($user && $this->_getCurrentUserCanLeave($query->getId())) { + if ($user && $this->_getCurrentUserCanLeave($query->id)) { $showLeaveQueryButton = true; } else { $showLeaveQueryButton = false; @@ -528,22 +525,20 @@ public function readQuery($args, $request) public function participants($args, $request) { $query = $this->getQuery(); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $context = $request->getContext(); $user = $request->getUser(); $participants = []; - foreach ($queryDao->getParticipantIds($query->getId()) as $userId) { - $user = Repo::user()->get($userId); - if ($user) { - $participants[] = $user; + $queryParticipants = $query->queryParticipants; + foreach ($queryParticipants as $participant) { + if ($participant->user) { + $participants[] = $participant->user; } } $templateMgr = TemplateManager::getManager($request); $templateMgr->assign('participants', $participants); - if ($user && $this->_getCurrentUserCanLeave($query->getId())) { + if ($user && $this->_getCurrentUserCanLeave($query->id)) { $showLeaveQueryButton = true; } else { $showLeaveQueryButton = false; @@ -566,7 +561,7 @@ public function participants($args, $request) public function editQuery($args, $request) { $query = $this->getQuery(); - if (!$this->getAccessHelper()->getCanEdit($query->getId())) { + if (!$this->getAccessHelper()->getCanEdit($query->id)) { return new JSONMessage(false); } @@ -576,7 +571,7 @@ public function editQuery($args, $request) $this->getAssocType(), $this->getAssocId(), $this->getStageId(), - $query->getId() + $query->id ); $queryForm->initData(); return new JSONMessage(true, $queryForm->fetch($request, null, false, $this->getRequestArgs())); @@ -593,19 +588,19 @@ public function editQuery($args, $request) public function updateQuery($args, $request) { $query = $this->getQuery(); - if (!$this->getAccessHelper()->getCanEdit($query->getId())) { + if (!$this->getAccessHelper()->getCanEdit($query->id)) { return new JSONMessage(false); } - /** @var QueryDAO */ - $queryDao = DAORegistry::getDAO('QueryDAO'); - $oldParticipantIds = $queryDao->getParticipantIds($query->getId()); + $oldParticipantIds = QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(); $queryForm = new QueryForm( $request, $this->getAssocType(), $this->getAssocId(), $this->getStageId(), - $query->getId() + $query->id ); $queryForm->readInputData(); @@ -641,7 +636,7 @@ public function updateQuery($args, $request) /** @var NotificationSubscriptionSettingsDAO */ $notificationSubscriptionSettingsDao = DAORegistry::getDAO('NotificationSubscriptionSettingsDAO'); - $note = $query->getHeadNote(); + $note = Repo::note()->getHeadNote($query->id); $submission = $this->getSubmission(); // Find attachments if any @@ -662,7 +657,7 @@ public function updateQuery($args, $request) Notification::NOTIFICATION_TYPE_NEW_QUERY, $request->getContext()->getId(), PKPApplication::ASSOC_TYPE_QUERY, - $query->getId(), + $query->id, Notification::NOTIFICATION_LEVEL_TASK ); @@ -692,7 +687,7 @@ public function updateQuery($args, $request) Repo::emailLogEntry()->logMailable(SubmissionEmailLogEventType::DISCUSSION_NOTIFY, $mailable, $submission); } - return \PKP\db\DAO::getDataChangedEvent($query->getId()); + return DAO::getDataChangedEvent($query->id); } // If this was new (placeholder) query that didn't validate, remember whether or not @@ -708,7 +703,7 @@ public function updateQuery($args, $request) false, array_merge( $this->getRequestArgs(), - ['queryId' => $query->getId()] + ['queryId' => $query->id] ) ) ); @@ -727,8 +722,9 @@ public function leaveQuery($args, $request) $queryId = $args['queryId']; $user = $request->getUser(); if ($user && $this->_getCurrentUserCanLeave($queryId)) { - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $queryDao->removeParticipant($queryId, $user->getId()); + QueryParticipant::withQueryId($queryId) + ->withUserId($user->getId()) + ->delete(); $json = new JSONMessage(); $json->setEvent('user-left-discussion'); } else { @@ -750,8 +746,9 @@ public function _getCurrentUserCanLeave($queryId) if (!count(array_intersect([Role::ROLE_ID_MANAGER, Role::ROLE_ID_SITE_ADMIN, ], $userRoles))) { return false; } - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $participantIds = $queryDao->getParticipantIds($queryId); + $participantIds = QueryParticipant::withQueryId($queryId) + ->pluck('user_id') + ->all(); if (count($participantIds) < 3) { return false; } diff --git a/controllers/grid/queries/QueryNotesGridHandler.php b/controllers/grid/queries/QueryNotesGridHandler.php index 357af98335d..18fc5b545f9 100644 --- a/controllers/grid/queries/QueryNotesGridHandler.php +++ b/controllers/grid/queries/QueryNotesGridHandler.php @@ -34,7 +34,7 @@ use PKP\notification\Notification; use PKP\notification\NotificationSubscriptionSettingsDAO; use PKP\query\Query; -use PKP\query\QueryDAO; +use PKP\query\QueryParticipant; use PKP\security\authorization\QueryAccessPolicy; use PKP\security\Role; use PKP\submissionFile\SubmissionFile; @@ -168,7 +168,7 @@ public function getRequestArgs() return [ 'submissionId' => $this->getSubmission()->getId(), 'stageId' => $this->getStageId(), - 'queryId' => $this->getQuery()->getId(), + 'queryId' => $this->getQuery()->id, ]; } @@ -185,7 +185,7 @@ public function getRequestArgs() */ public function loadData($request, $filter = null) { - return Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $this->getQuery()->getId()) + return Note::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $this->getQuery()->id) ->withSort(Note::NOTE_ORDER_DATE_CREATED, DAO::SORT_DIRECTION_ASC) ->lazy() ->filter(function (Note $note) use ($request) { @@ -222,7 +222,7 @@ public function insertNote($args, $request) if ($queryNoteForm->validate()) { $note = $queryNoteForm->execute(); $this->insertedNoteNotify($note); - return DAO::getDataChangedEvent($this->getQuery()->getId()); + return DAO::getDataChangedEvent($this->getQuery()->id); } else { return new JSONMessage(true, $queryNoteForm->fetch($request)); } @@ -258,7 +258,7 @@ public function deleteNote($args, $request) $query = $this->getQuery(); $note = Note::find((int) $request->getUserVar('noteId')); - if (!$request->checkCSRF() || $note?->assocType != Application::ASSOC_TYPE_QUERY || $note?->assocId != $query->getId()) { + if (!$request->checkCSRF() || $note?->assocType != Application::ASSOC_TYPE_QUERY || $note?->assocId != $query->id) { // The note didn't exist or has the wrong assoc info. return new JSONMessage(false); } @@ -278,13 +278,12 @@ public function deleteNote($args, $request) protected function insertedNoteNotify(Note $note): void { $notificationManager = new NotificationManager(); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->getById($note->assocId); + $query = Query::find($note->assocId); $sender = $note->user; $request = Application::get()->getRequest(); $context = $request->getContext(); $submission = $this->getSubmission(); - $title = $query->getHeadNote()->title; + $title = Repo::note()->getHeadNote($query->id)->title; /** @var NotificationSubscriptionSettingsDAO $notificationSubscriptionSettingsDao */ $notificationSubscriptionSettingsDao = DAORegistry::getDAO('NotificationSubscriptionSettingsDAO'); @@ -297,10 +296,12 @@ protected function insertedNoteNotify(Note $note): void [$note->id] )->filterBySubmissionIds([$submission->getId()]) ->getMany(); - - foreach ($queryDao->getParticipantIds($query->getId()) as $userId) { + $participantIds = QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(); + foreach ($participantIds as $userId) { // Delete any prior notifications of the same type (e.g. prior "new" comments) - Notification::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->getId()) + Notification::withAssoc(PKPApplication::ASSOC_TYPE_QUERY, $query->id) ->withUserId($userId) ->withType(Notification::NOTIFICATION_TYPE_QUERY_ACTIVITY) ->withContextId($context->getId()) @@ -323,7 +324,7 @@ protected function insertedNoteNotify(Note $note): void Notification::NOTIFICATION_TYPE_QUERY_ACTIVITY, $request->getContext()->getId(), PKPApplication::ASSOC_TYPE_QUERY, - $query->getId(), + $query->id, Notification::NOTIFICATION_LEVEL_TASK ); diff --git a/controllers/grid/queries/QueryNotesGridRow.php b/controllers/grid/queries/QueryNotesGridRow.php index 705b230afdd..759ea80ad83 100644 --- a/controllers/grid/queries/QueryNotesGridRow.php +++ b/controllers/grid/queries/QueryNotesGridRow.php @@ -16,6 +16,7 @@ namespace PKP\controllers\grid\queries; +use APP\facades\Repo; use PKP\controllers\grid\GridRow; use PKP\linkAction\LinkAction; use PKP\linkAction\request\RemoteActionConfirmationModal; @@ -63,7 +64,7 @@ public function initialize($request, $template = null) // Is this a new row or an existing row? $rowId = abs($this->getId()); - $headNote = $this->getQuery()->getHeadNote(); + $headNote = Repo::note()->getHeadNote($this->getQuery()->id); if ($rowId > 0 && $headNote?->id != $rowId) { // Only add row actions if this is an existing row $router = $request->getRouter(); diff --git a/controllers/grid/queries/QueryTitleGridColumn.php b/controllers/grid/queries/QueryTitleGridColumn.php index ed5a9f97588..c38073f113a 100644 --- a/controllers/grid/queries/QueryTitleGridColumn.php +++ b/controllers/grid/queries/QueryTitleGridColumn.php @@ -3,8 +3,8 @@ /** * @file controllers/grid/queries/QueryTitleGridColumn.php * - * Copyright (c) 2014-2021 Simon Fraser University - * Copyright (c) 2000-2021 John Willinsky + * Copyright (c) 2014-2024 Simon Fraser University + * Copyright (c) 2000-2024 John Willinsky * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING. * * @class QueryTitleGridColumn @@ -16,6 +16,7 @@ namespace PKP\controllers\grid\queries; +use APP\facades\Repo; use PKP\controllers\grid\ColumnBasedGridCellProvider; use PKP\controllers\grid\GridColumn; use PKP\controllers\grid\GridHandler; @@ -77,13 +78,13 @@ public function getCellActions($request, $row, $position = GridHandler::GRID_ACT { // Retrieve the submission file. $query = $row->getData(); - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); // Create the cell action to download a file. $router = $request->getRouter(); $actionArgs = array_merge( $this->_actionArgs, - ['queryId' => $query->getId()] + ['queryId' => $query->id] ); return array_merge( diff --git a/controllers/grid/queries/form/QueryForm.php b/controllers/grid/queries/form/QueryForm.php index 03a64eced8d..3f623f6db32 100644 --- a/controllers/grid/queries/form/QueryForm.php +++ b/controllers/grid/queries/form/QueryForm.php @@ -24,11 +24,15 @@ use PKP\controllers\grid\queries\traits\StageMailable; use PKP\core\PKPApplication; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; use PKP\form\Form; +use PKP\form\validation\FormValidator; +use PKP\form\validation\FormValidatorCSRF; +use PKP\form\validation\FormValidatorCustom; +use PKP\form\validation\FormValidatorPost; use PKP\note\Note; +use PKP\notification\Notification; use PKP\query\Query; -use PKP\query\QueryDAO; +use PKP\query\QueryParticipant; use PKP\security\Role; use PKP\stageAssignment\StageAssignment; use PKP\submission\reviewAssignment\ReviewAssignment; @@ -67,44 +71,47 @@ public function __construct($request, $assocType, $assocId, $stageId, $queryId = parent::__construct('controllers/grid/queries/form/queryForm.tpl'); $this->setStageId($stageId); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ if (!$queryId) { $this->_isNew = true; // Create a query - $query = $queryDao->newDataObject(); - $query->setAssocType($assocType); - $query->setAssocId($assocId); - $query->setStageId($stageId); - $query->setSequence(REALLY_BIG_NUMBER); - $queryDao->insertObject($query); - $queryDao->resequence($assocType, $assocId); + $query = Query::create([ + 'assocType' => $assocType, + 'assocId' => $assocId, + 'stageId' => $stageId, + 'seq' => REALLY_BIG_NUMBER + ]); + + Repo::query()->resequence($assocType, $assocId); // Add the current user as a participant by default. - $queryDao->insertParticipant($query->getId(), $request->getUser()->getId()); + QueryParticipant::create([ + 'queryId' => $query->id, + 'userId' => $request->getUser()->getId() + ]); Note::create([ 'userId' => $request->getUser()->getId(), 'assocType' => Application::ASSOC_TYPE_QUERY, - 'assocId' => $query->getId(), + 'assocId' => $query->id, ]); } else { - $query = $queryDao->getById($queryId, $assocType, $assocId); + $query = Query::find($queryId); assert(isset($query)); // New queries will not have a head note. - $this->_isNew = !$query->getHeadNote(); + $this->_isNew = !Repo::note()->getHeadNote($query->id); } $this->setQuery($query); // Validation checks for this form - $this->addCheck(new \PKP\form\validation\FormValidatorCustom($this, 'users', 'required', 'stageParticipants.notify.warning', function ($users) { + $this->addCheck(new FormValidatorCustom($this, 'users', 'required', 'stageParticipants.notify.warning', function ($users) { return count($users) > 1; })); - $this->addCheck(new \PKP\form\validation\FormValidator($this, 'subject', 'required', 'submission.queries.subjectRequired')); - $this->addCheck(new \PKP\form\validation\FormValidator($this, 'comment', 'required', 'submission.queries.messageRequired')); - $this->addCheck(new \PKP\form\validation\FormValidatorPost($this)); - $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); + $this->addCheck(new FormValidator($this, 'subject', 'required', 'submission.queries.subjectRequired')); + $this->addCheck(new FormValidator($this, 'comment', 'required', 'submission.queries.messageRequired')); + $this->addCheck(new FormValidatorPost($this)); + $this->addCheck(new FormValidatorCSRF($this)); } // @@ -208,14 +215,15 @@ public function setAssocId($assocId) */ public function initData() { - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ if ($query = $this->getQuery()) { - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); $this->_data = [ - 'queryId' => $query->getId(), + 'queryId' => $query->id, 'subject' => $headNote?->title, 'comment' => $headNote?->contents, - 'userIds' => $queryDao->getParticipantIds($query->getId()), + 'userIds' => QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(), 'template' => null, ]; } else { @@ -237,7 +245,7 @@ public function initData() public function fetch($request, $template = null, $display = false, $actionArgs = []) { $query = $this->getQuery(); - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); $user = $request->getUser(); $context = $request->getContext(); @@ -248,16 +256,16 @@ public function fetch($request, $template = null, $display = false, $actionArgs 'actionArgs' => $actionArgs, 'csrfToken' => $request->getSession()->token(), 'stageId' => $this->getStageId(), - 'assocId' => $query->getAssocId(), - 'assocType' => $query->getAssocType(), + 'assocId' => $query->assocId, + 'assocType' => $query->assocType, ]); // Queries only support Application::ASSOC_TYPE_SUBMISSION so far - if ($query->getAssocType() !== PKPApplication::ASSOC_TYPE_SUBMISSION) { + if ($query->assocType !== PKPApplication::ASSOC_TYPE_SUBMISSION) { return parent::fetch($request, $template, $display); } - $submission = Repo::submission()->get($query->getAssocId()); + $submission = Repo::submission()->get($query->assocId); // Add the templates that can be used for this discussion $templateKeySubjectPairs = []; @@ -280,8 +288,8 @@ public function fetch($request, $template = null, $display = false, $actionArgs $templateMgr->assign('templates', $templateKeySubjectPairs); // Get currently selected participants in the query - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $assignedParticipants = $query->getId() ? $queryDao->getParticipantIds($query->getId()) : []; + + $assignedParticipants = $query->id ? QueryParticipant::withQueryId($query->id)->pluck('user_id')->all() : []; // Always include current user, even if not with a stage assignment $includeUsers[] = $user->getId(); @@ -293,8 +301,8 @@ public function fetch($request, $template = null, $display = false, $actionArgs $assignedRoles = (function () use ($query, $user) { $assignedRoles = []; // Replaces StageAssignmentDAO::getBySubmissionAndStageId - $usersAssignments = StageAssignment::withSubmissionIds([$query->getAssocId()]) - ->withStageIds([$query->getStageId()]) + $usersAssignments = StageAssignment::withSubmissionIds([$query->assocId]) + ->withStageIds([$query->stageId]) ->withUserId($user->getId()) ->get(); @@ -305,7 +313,7 @@ public function fetch($request, $template = null, $display = false, $actionArgs return $assignedRoles; })(); - if ($query->getStageId() == WORKFLOW_STAGE_ID_EXTERNAL_REVIEW || $query->getStageId() == WORKFLOW_STAGE_ID_INTERNAL_REVIEW) { + if ($query->stageId == WORKFLOW_STAGE_ID_EXTERNAL_REVIEW || $query->stageId == WORKFLOW_STAGE_ID_INTERNAL_REVIEW) { // Get all review assignments for current submission $reviewAssignments = Repo::reviewAssignment()->getCollector()->filterBySubmissionIds([$submission->getId()])->getMany(); @@ -321,7 +329,7 @@ public function fetch($request, $template = null, $display = false, $actionArgs if ($reviewAssignment->getReviewerId() == $user->getId()) { if ($reviewAssignment->getReviewMethod() != ReviewAssignment::SUBMISSION_REVIEW_METHOD_OPEN) { // Replaces StageAssignmentDAO::getBySubmissionAndRoleId - $excludeUsers = StageAssignment::withSubmissionIds([$query->getAssocId()]) + $excludeUsers = StageAssignment::withSubmissionIds([$query->assocId]) ->withRoleIds([Role::ROLE_ID_AUTHOR]) ->withUserId($user->getId()) ->get() @@ -345,7 +353,7 @@ public function fetch($request, $template = null, $display = false, $actionArgs ->filterByContextIds([$context->getId()]) ->limit(100) ->offset(0) - ->assignedTo($query->getAssocId(), $query->getStageId()) + ->assignedTo($query->assocId, $query->stageId) ->excludeUserIds($excludeUsers) ->getMany(); @@ -358,8 +366,8 @@ public function fetch($request, $template = null, $display = false, $actionArgs $userRoles = []; // Replaces StageAssignmentDAO::getBySubmissionAndStageId - $userAssignments = StageAssignment::withSubmissionIds([$query->getAssocId()]) - ->withStageIds([$query->getStageId()]) + $userAssignments = StageAssignment::withSubmissionIds([$query->assocId]) + ->withStageIds([$query->stageId]) ->withUserId($user->getId()) ->get(); @@ -428,12 +436,12 @@ public function validate($callHooks = true) // In other stages validate that participants are assigned to that stage. $query = $this->getQuery(); // Queries only support Application::ASSOC_TYPE_SUBMISSION so far (see above) - if ($query->getAssocType() == Application::ASSOC_TYPE_SUBMISSION) { + if ($query->assocType == Application::ASSOC_TYPE_SUBMISSION) { $request = Application::get()->getRequest(); $user = $request->getUser(); $context = $request->getContext(); - $submissionId = $query->getAssocId(); - $stageId = $query->getStageId(); + $submissionId = $query->assocId; + $stageId = $query->stageId; // get the selected participants $newParticipantIds = (array) $this->getData('users'); @@ -509,37 +517,43 @@ public function validate($callHooks = true) */ public function execute(...$functionArgs) { - $request = Application::get()->getRequest(); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ $query = $this->getQuery(); - $headNote = $query->getHeadNote(); + $headNote = Repo::note()->getHeadNote($query->id); $headNote->title = $this->getData('subject'); $headNote->contents = $this->getData('comment'); - $headNote->save(); - $queryDao->updateObject($query); + $query->update(); // Update participants - $oldParticipantIds = $queryDao->getParticipantIds($query->getId()); + $oldParticipantIds = QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(); + $newParticipantIds = $this->getData('users'); - $queryDao->removeAllParticipants($query->getId()); + QueryParticipant::withQueryId($query->id) + ->delete(); + $addParticipants = []; foreach ($newParticipantIds as $userId) { - $queryDao->insertParticipant($query->getId(), $userId); + $addParticipants[] = ([ + 'query_id' => $query->id, + 'user_id' => $userId + ]); } + QueryParticipant::insert($addParticipants); $removed = array_diff($oldParticipantIds, $newParticipantIds); foreach ($removed as $userId) { // Delete this users' notifications relating to this query - Notification::withAssoc(Application::ASSOC_TYPE_QUERY, $query->getId()) + Notification::withAssoc(Application::ASSOC_TYPE_QUERY, $query->id) ->withUserId($userId) ->delete(); } // Stamp the submission status modification date. - if ($query->getAssocType() == Application::ASSOC_TYPE_SUBMISSION) { - $submission = Repo::submission()->get($query->getAssocId()); + if ($query->assocType == Application::ASSOC_TYPE_SUBMISSION) { + $submission = Repo::submission()->get($query->assocId); $submission->stampLastActivity(); Repo::submission()->dao->update($submission); } diff --git a/controllers/grid/queries/form/QueryNoteForm.php b/controllers/grid/queries/form/QueryNoteForm.php index d84ee642311..378535dd344 100644 --- a/controllers/grid/queries/form/QueryNoteForm.php +++ b/controllers/grid/queries/form/QueryNoteForm.php @@ -17,13 +17,15 @@ namespace PKP\controllers\grid\queries\form; use APP\core\Application; +use APP\facades\Repo; use APP\template\TemplateManager; -use PKP\core\Core; -use PKP\db\DAORegistry; use PKP\form\Form; +use PKP\form\validation\FormValidator; +use PKP\form\validation\FormValidatorCSRF; +use PKP\form\validation\FormValidatorPost; use PKP\note\Note; use PKP\query\Query; -use PKP\query\QueryDAO; +use PKP\query\QueryParticipant; use PKP\user\User; class QueryNoteForm extends Form @@ -58,7 +60,7 @@ public function __construct($actionArgs, $query, $user, $noteId = null) // Create a new (placeholder) note. $note = new Note; $note->assocType = Application::ASSOC_TYPE_QUERY; - $note->assocId = $query->getId(); + $note->assocId = $query->id; $note->userId = $user->getId(); $note->save(); $this->_noteId = $note->id; @@ -69,9 +71,9 @@ public function __construct($actionArgs, $query, $user, $noteId = null) } // Validation checks for this form - $this->addCheck(new \PKP\form\validation\FormValidator($this, 'comment', 'required', 'submission.queries.messageRequired')); - $this->addCheck(new \PKP\form\validation\FormValidatorPost($this)); - $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); + $this->addCheck(new FormValidator($this, 'comment', 'required', 'submission.queries.messageRequired')); + $this->addCheck(new FormValidatorPost($this)); + $this->addCheck(new FormValidatorCSRF($this)); } // @@ -140,23 +142,26 @@ public function execute(...$functionArgs) $note->contents = $this->getData('comment'); $note->save(); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - // Check whether the query needs re-opening $query = $this->getQuery(); - if ($query->getIsClosed()) { - $headNote = $query->getHeadNote(); + if ($query->closed) { + $headNote = Repo::note()->getHeadNote($query->id); if ($user->getId() != $headNote->userId) { // Re-open the query. - $query->setIsClosed(false); - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $queryDao->updateObject($query); + $query->closed = false; + $query->save(); } } // Always include current user to query participants - if (!in_array($user->getId(), $queryDao->getParticipantIds($query->getId()))) { - $queryDao->insertParticipant($query->getId(), $user->getId()); + $participantIds = QueryParticipant::withQueryId($query->id) + ->pluck('user_id') + ->all(); + if (!in_array($user->getId(), $participantIds)) { + QueryParticipant::create([ + 'queryId' => $query->id, + 'userId' => $user->getId() + ]); } parent::execute(...$functionArgs); diff --git a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php index 8309318084c..55ffecfaaa6 100644 --- a/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php +++ b/controllers/grid/users/stageParticipant/form/PKPStageParticipantNotifyForm.php @@ -27,13 +27,16 @@ use PKP\core\Core; use PKP\core\PKPApplication; use PKP\core\PKPRequest; -use PKP\db\DAORegistry; use PKP\form\Form; +use PKP\form\validation\FormValidator; +use PKP\form\validation\FormValidatorCSRF; +use PKP\form\validation\FormValidatorPost; use PKP\log\event\EventLogEntry; use PKP\log\SubmissionEmailLogEventType; use PKP\note\Note; use PKP\notification\Notification; -use PKP\query\QueryDAO; +use PKP\query\Query; +use PKP\query\QueryParticipant; use PKP\security\Role; use PKP\security\Validation; use Symfony\Component\Mailer\Exception\TransportException; @@ -77,11 +80,11 @@ public function __construct($itemId, $itemType, $stageId, $template = null) // Some other forms (e.g. the Add Participant form) subclass this form and // may not enforce the sending of an email. if ($this->isMessageRequired()) { - $this->addCheck(new \PKP\form\validation\FormValidator($this, 'message', 'required', 'stageParticipants.notify.warning')); + $this->addCheck(new FormValidator($this, 'message', 'required', 'stageParticipants.notify.warning')); } - $this->addCheck(new \PKP\form\validation\FormValidator($this, 'userId', 'required', 'stageParticipants.notify.warning')); - $this->addCheck(new \PKP\form\validation\FormValidatorPost($this)); - $this->addCheck(new \PKP\form\validation\FormValidatorCSRF($this)); + $this->addCheck(new FormValidator($this, 'userId', 'required', 'stageParticipants.notify.warning')); + $this->addCheck(new FormValidatorPost($this)); + $this->addCheck(new FormValidatorCSRF($this)); } /** @@ -175,19 +178,25 @@ public function sendMessage(int $userId, Submission $submission, Request $reques } // Create a query - $queryDao = DAORegistry::getDAO('QueryDAO'); /** @var QueryDAO $queryDao */ - $query = $queryDao->newDataObject(); - $query->setAssocType(PKPApplication::ASSOC_TYPE_SUBMISSION); - $query->setAssocId($submission->getId()); - $query->setStageId($this->_stageId); - $query->setSequence(REALLY_BIG_NUMBER); - $queryDao->insertObject($query); - $queryDao->resequence(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId()); + $query = Query::create([ + 'assocType' => PKPApplication::ASSOC_TYPE_SUBMISSION, + 'assocId' => $submission->getId(), + 'stageId' => $this->_stageId, + 'seq' => REALLY_BIG_NUMBER + ]); + + Repo::query()->resequence(PKPApplication::ASSOC_TYPE_SUBMISSION, $submission->getId()); // Add the current user and message recipient as participants. - $queryDao->insertParticipant($query->getId(), $user->getId()); + QueryParticipant::create([ + 'queryId' => $query->id, + 'userId' => $user->getId() + ]); if ($user->getId() != $request->getUser()->getId()) { - $queryDao->insertParticipant($query->getId(), $request->getUser()->getId()); + QueryParticipant::create([ + 'queryId' => $query->id, + 'userId' => $request->getUser()->getId() + ]); } // Populate mailable with data before compiling headNote @@ -205,7 +214,7 @@ public function sendMessage(int $userId, Submission $submission, Request $reques $headNote = Note::create([ 'userId' => $request->getUser()->getId(), 'assocType' => PKPApplication::ASSOC_TYPE_QUERY, - 'assocId' => $query->getId(), + 'assocId' => $query->id, 'title' => Mail::compileParams( $template->getLocalizedData('subject'), $mailable->getData() @@ -224,7 +233,7 @@ public function sendMessage(int $userId, Submission $submission, Request $reques Notification::NOTIFICATION_TYPE_NEW_QUERY, $request->getContext()->getId(), PKPApplication::ASSOC_TYPE_QUERY, - $query->getId(), + $query->id, Notification::NOTIFICATION_LEVEL_TASK ); diff --git a/templates/controllers/grid/queries/readQuery.tpl b/templates/controllers/grid/queries/readQuery.tpl index 5c6de7f8682..80620772af6 100644 --- a/templates/controllers/grid/queries/readQuery.tpl +++ b/templates/controllers/grid/queries/readQuery.tpl @@ -13,8 +13,8 @@ $('#readQueryContainer').pkpHandler( '$.pkp.controllers.grid.queries.ReadQueryHandler', {ldelim} - fetchNoteFormUrl: {url|json_encode router=PKP\core\PKPApplication::ROUTE_COMPONENT component=$queryNotesGridHandlerName op="addNote" params=$requestArgs queryId=$query->getId() escape=false}, - fetchParticipantsListUrl: {url|json_encode router=PKP\core\PKPApplication::ROUTE_COMPONENT component="grid.queries.QueriesGridHandler" op="participants" params=$requestArgs queryId=$query->getId() escape=false} + fetchNoteFormUrl: {url|json_encode router=PKP\core\PKPApplication::ROUTE_COMPONENT component=$queryNotesGridHandlerName op="addNote" params=$requestArgs queryId=$query->id escape=false}, + fetchParticipantsListUrl: {url|json_encode router=PKP\core\PKPApplication::ROUTE_COMPONENT component="grid.queries.QueriesGridHandler" op="participants" params=$requestArgs queryId=$query->id escape=false} {rdelim} ); {rdelim}); @@ -29,7 +29,7 @@
    - {capture assign=queryNotesGridUrl}{url router=PKP\core\PKPApplication::ROUTE_COMPONENT component=$queryNotesGridHandlerName op="fetchGrid" params=$requestArgs queryId=$query->getId() escape=false}{/capture} + {capture assign=queryNotesGridUrl}{url router=PKP\core\PKPApplication::ROUTE_COMPONENT component=$queryNotesGridHandlerName op="fetchGrid" params=$requestArgs queryId=$query->id escape=false}{/capture} {load_url_in_div id="queryNotesGrid" url=$queryNotesGridUrl}