From 6e5194f24ff91fa3b50ddb3d2d4d76ec4a734b08 Mon Sep 17 00:00:00 2001 From: Nate Wright Date: Fri, 11 Aug 2017 11:31:33 +0100 Subject: [PATCH 1/7] pkp/pkp-lib#2612 Support submission list filtering by stage, section and incomplete submissions --- .../PKPBackendSubmissionsHandler.inc.php | 6 +- classes/services/PKPSubmissionService.inc.php | 3 + .../PKPSubmissionListQueryBuilder.inc.php | 43 +++++ ....php => PKPSubmissionsListHandler.inc.php} | 17 +- .../SelectSubmissionsListHandler.inc.php | 6 +- js/controllers/list/ListPanel.vue | 5 +- .../submissions/SubmissionsListFilter.vue | 153 ++++++++++++++++++ .../list/submissions/SubmissionsListPanel.vue | 42 ++++- pages/dashboard/DashboardHandler.inc.php | 2 +- 9 files changed, 261 insertions(+), 16 deletions(-) rename controllers/list/submissions/{SubmissionsListHandler.inc.php => PKPSubmissionsListHandler.inc.php} (90%) create mode 100644 js/controllers/list/submissions/SubmissionsListFilter.vue diff --git a/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php b/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php index d7e96836eb7..fb8de52e761 100644 --- a/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php +++ b/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php @@ -86,8 +86,9 @@ public function getSubmissions($slimRequest, $response, $args) { foreach ($params as $param => $val) { switch ($param) { - // Always convert status to array + // Always convert status and stageIds to array case 'status': + case 'stageIds': if (is_string($val) && strpos($val, ',') > -1) { $val = explode(',', $val); } elseif (!is_array($val)) { @@ -119,6 +120,9 @@ public function getSubmissions($slimRequest, $response, $args) { case 'orderDirection': $params[$param] = $val === 'ASC' ? $val : 'DESC'; break; + + case 'isIncomplete': + $params[$param] = true; } } diff --git a/classes/services/PKPSubmissionService.inc.php b/classes/services/PKPSubmissionService.inc.php index c948b97ce98..4f3fe3ed4a2 100644 --- a/classes/services/PKPSubmissionService.inc.php +++ b/classes/services/PKPSubmissionService.inc.php @@ -55,6 +55,7 @@ public function getSubmissionList($contextId, $args = array()) { 'searchPhrase' => null, 'count' => 20, 'offset' => 0, + 'isIncomplete' => false, ); $args = array_merge($defaultArgs, $args); @@ -64,6 +65,8 @@ public function getSubmissionList($contextId, $args = array()) { ->orderBy($args['orderBy'], $args['orderDirection']) ->assignedTo($args['assignedTo']) ->filterByStatus($args['status']) + ->filterByStageIds($args['stageIds']) + ->filterByIncomplete($args['isIncomplete']) ->searchPhrase($args['searchPhrase']); \HookRegistry::call('Submission::getSubmissionList::queryBuilder', array(&$submissionListQB, $contextId, $args)); diff --git a/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php b/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php index c0ad3b94419..d3ca1809554 100644 --- a/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php +++ b/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php @@ -34,6 +34,9 @@ abstract class PKPSubmissionListQueryBuilder extends BaseQueryBuilder { /** @var array list of statuses */ protected $statuses = null; + /** @var array list of stage ids */ + protected $stageIds = null; + /** @var int user ID */ protected $assigneeId = null; @@ -43,6 +46,9 @@ abstract class PKPSubmissionListQueryBuilder extends BaseQueryBuilder { /** @var bool whether to return only a count of results */ protected $countOnly = null; + /** @var bool whether to return only incomplete results */ + protected $isIncomplete = false; + /** * Constructor * @@ -88,6 +94,33 @@ public function filterByStatus($statuses) { return $this; } + /** + * Set stage filter + * + * @param int|array $stageIds + * + * @return \OJS\Services\QueryBuilders\SubmissionListQueryBuilder + */ + public function filterByStageIds($stageIds) { + if (!is_null($stageIds) && !is_array($stageIds)) { + $stageIds = array($stageIds); + } + $this->stageIds = $stageIds; + return $this; + } + + /** + * Set incomplete submissions filter + * + * @param boolean $isIncomplete + * + * @return \OJS\Services\QueryBuilders\SubmissionListQueryBuilder + */ + public function filterByIncomplete($isIncomplete) { + $this->isIncomplete = $isIncomplete; + return $this; + } + /** * Limit results to a specific user's submissions * @@ -153,6 +186,16 @@ public function get() { $q->whereIn('s.status', $this->statuses); } + // stage ids + if (!is_null($this->stageIds)) { + $q->whereIn('s.stage_id', $this->stageIds); + } + + // incomplete submissions + if ($this->isIncomplete) { + $q->where('s.submission_progress', '>', 0); + } + // assigned to if (!is_null($this->assigneeId) && ($this->assigneeId !== -1)) { $assigneeId = $this->assigneeId; diff --git a/controllers/list/submissions/SubmissionsListHandler.inc.php b/controllers/list/submissions/PKPSubmissionsListHandler.inc.php similarity index 90% rename from controllers/list/submissions/SubmissionsListHandler.inc.php rename to controllers/list/submissions/PKPSubmissionsListHandler.inc.php index 7016ffbeec5..1d9c8ed9225 100644 --- a/controllers/list/submissions/SubmissionsListHandler.inc.php +++ b/controllers/list/submissions/PKPSubmissionsListHandler.inc.php @@ -1,12 +1,12 @@ _getParams; + $config['stages'] = $this->getWorkflowStages(); + // Load grid localisation files AppLocale::requireComponents(LOCALE_COMPONENT_PKP_GRID); AppLocale::requireComponents(LOCALE_COMPONENT_PKP_SUBMISSION); @@ -128,6 +130,8 @@ public function getConfig() { 'filesPrepared' => __('submission.list.filesPrepared'), 'discussions' => __('submission.list.discussions'), 'incompleteSubmissionNotice' => __('submission.list.incompleteSubmissionNotice'), + 'stages' => __('settings.roles.stages'), + 'sections' => __('section.sections'), ); // Attach a CSRF token for post requests @@ -159,4 +163,11 @@ public function getItems() { ->get('submission') ->getSubmissionList($contextId, $params); } + + /** + * Get an array of workflow stages supported by the current app + * + * @return array + */ + abstract function getWorkflowStages(); } diff --git a/controllers/list/submissions/SelectSubmissionsListHandler.inc.php b/controllers/list/submissions/SelectSubmissionsListHandler.inc.php index a2f888f0f41..4526cb1c963 100644 --- a/controllers/list/submissions/SelectSubmissionsListHandler.inc.php +++ b/controllers/list/submissions/SelectSubmissionsListHandler.inc.php @@ -11,7 +11,7 @@ * * @brief A list handler for selecting submissions */ -import('lib.pkp.controllers.list.submissions.SubmissionsListHandler'); +import('controllers.list.submissions.SubmissionsListHandler'); class SelectSubmissionsListHandler extends SubmissionsListHandler { @@ -23,7 +23,7 @@ class SelectSubmissionsListHandler extends SubmissionsListHandler { public $_inputName = 'selectedSubmissions'; /** - * @see SubmissionsListHandler + * @copydoc SubmissionsListHandler */ public function init( $args = array() ) { parent::init($args); @@ -31,7 +31,7 @@ public function init( $args = array() ) { } /** - * @see SubmissionsListHandler + * @copydoc SubmissionsListHandler */ public function getConfig() { $config = parent::getConfig(); diff --git a/js/controllers/list/ListPanel.vue b/js/controllers/list/ListPanel.vue index 16cd33dcfb7..a0b6f405507 100644 --- a/js/controllers/list/ListPanel.vue +++ b/js/controllers/list/ListPanel.vue @@ -167,7 +167,10 @@ export default { * Update filter parameters */ updateFilter: function(params) { - this.filterParams = params; + this.filterParams = {}; + this.$nextTick(function() { + this.filterParams = params; + }); }, /** diff --git a/js/controllers/list/submissions/SubmissionsListFilter.vue b/js/controllers/list/submissions/SubmissionsListFilter.vue new file mode 100644 index 00000000000..c3f182a06d6 --- /dev/null +++ b/js/controllers/list/submissions/SubmissionsListFilter.vue @@ -0,0 +1,153 @@ + + + diff --git a/js/controllers/list/submissions/SubmissionsListPanel.vue b/js/controllers/list/submissions/SubmissionsListPanel.vue index 26eea373a7f..a2cb705d3de 100644 --- a/js/controllers/list/submissions/SubmissionsListPanel.vue +++ b/js/controllers/list/submissions/SubmissionsListPanel.vue @@ -3,6 +3,12 @@
{{ i18n.title }}
- +
+ +
+
-
-
- {{ i18n.stages }} +
+
+ {{ filter.heading }}
    -
  • +
  • {{ stage.title }} + >{{ filterItem.title }} -
  • -
-
-
-
- {{ i18n.sections }} -
- -
-
-
    -
  • - {{ i18n.incomplete }} -
@@ -84,23 +39,17 @@ import ListPanelFilter from '../ListPanelFilter.vue'; export default { extends: ListPanelFilter, name: 'SubmissionsListFilter', - props: ['isVisible', 'stages', 'sections', 'i18n'], + props: ['isVisible', 'filters', 'i18n'], methods: { - /** - * Check if a filter is currently active - */ - isFilterActive: function(type, val) { - return this.activeFilters.filter(filter => { - return filter.type === type && filter.val === val; - }).length - }, - /** * Add a filter */ filterBy: function(type, val) { - // Deactivate the isIncomplete filter when anotherr filter is selected - if (this.isFilterActive('isIncomplete', true)) { + if (type === 'isIncomplete') { + this.filterByIncomplete(); + return; + // Deactivate the isIncomplete filter when any other filter is selected + } else if (this.isFilterActive('isIncomplete', true)) { this.clearFilter('isIncomplete', true); } if (this.isFilterActive(type, val)) { @@ -111,30 +60,6 @@ export default { this.filterList(this.compileFilterParams()); }, - /** - * Remove a filter - */ - clearFilter: function(type, val) { - this.activeFilters = this.activeFilters.filter(filter => { - return filter.type !== type || filter.val !== val; - }); - this.filterList(this.compileFilterParams()); - }, - - /** - * Compile active filters into filter parameters - */ - compileFilterParams: function() { - let params = {}; - for (var filter of this.activeFilters) { - if (params[filter.type] === undefined) { - params[filter.type] = []; - } - params[filter.type].push(filter.val); - } - return params; - }, - /** * Filter to show any incomplete submissions. * These are submissions which have been started but not fully submitted @@ -146,7 +71,8 @@ export default { return; } this.clearFilters(); - this.filterBy('isIncomplete', true); + this.activeFilters.push({type: 'isIncomplete', val: true}); + this.filterList(this.compileFilterParams()); }, }, }; diff --git a/js/controllers/list/submissions/SubmissionsListPanel.vue b/js/controllers/list/submissions/SubmissionsListPanel.vue index a2cb705d3de..96024147bd4 100644 --- a/js/controllers/list/submissions/SubmissionsListPanel.vue +++ b/js/controllers/list/submissions/SubmissionsListPanel.vue @@ -24,8 +24,7 @@ v-if="currentUserCanFilter" @filterList="updateFilter" :isVisible="isFilterVisible" - :stages="stages" - :sections="sections" + :filters="filters" :i18n="i18n" />
From 3990faafb2737040787836d9b2512a24c45d6ba1 Mon Sep 17 00:00:00 2001 From: Nate Wright Date: Fri, 11 Aug 2017 16:27:21 +0100 Subject: [PATCH 3/7] pkp/pkp-lib#2612 Remove OJS-specific string from shared SubmissionsListHandler --- controllers/list/submissions/PKPSubmissionsListHandler.inc.php | 1 - 1 file changed, 1 deletion(-) diff --git a/controllers/list/submissions/PKPSubmissionsListHandler.inc.php b/controllers/list/submissions/PKPSubmissionsListHandler.inc.php index 97d96a3ab53..f59784ed247 100644 --- a/controllers/list/submissions/PKPSubmissionsListHandler.inc.php +++ b/controllers/list/submissions/PKPSubmissionsListHandler.inc.php @@ -143,7 +143,6 @@ public function getConfig() { 'filesPrepared' => __('submission.list.filesPrepared'), 'discussions' => __('submission.list.discussions'), 'incompleteSubmissionNotice' => __('submission.list.incompleteSubmissionNotice'), - 'sections' => __('section.sections'), ); // Attach a CSRF token for post requests From 0d5f666d80add6c02401e810b7b9cd36b3bd8c97 Mon Sep 17 00:00:00 2001 From: Nate Wright Date: Mon, 14 Aug 2017 13:01:54 +0100 Subject: [PATCH 4/7] pkp/pkp-lib#2612 Add submission filter for overdue review assignments --- .../PKPBackendSubmissionsHandler.inc.php | 1 + classes/services/PKPSubmissionService.inc.php | 6 ++-- .../PKPSubmissionListQueryBuilder.inc.php | 32 +++++++++++++++++++ .../reviewRound/ReviewRound.inc.php | 2 +- .../PKPSubmissionsListHandler.inc.php | 16 +++++++--- .../submissions/SubmissionsListFilter.vue | 10 +++--- 6 files changed, 54 insertions(+), 13 deletions(-) diff --git a/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php b/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php index fb8de52e761..6aa9e448b9d 100644 --- a/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php +++ b/api/v1/_submissions/PKPBackendSubmissionsHandler.inc.php @@ -122,6 +122,7 @@ public function getSubmissions($slimRequest, $response, $args) { break; case 'isIncomplete': + case 'isOverdue': $params[$param] = true; } } diff --git a/classes/services/PKPSubmissionService.inc.php b/classes/services/PKPSubmissionService.inc.php index 4f3fe3ed4a2..d93a5128f4c 100644 --- a/classes/services/PKPSubmissionService.inc.php +++ b/classes/services/PKPSubmissionService.inc.php @@ -56,6 +56,7 @@ public function getSubmissionList($contextId, $args = array()) { 'count' => 20, 'offset' => 0, 'isIncomplete' => false, + 'isOverdue' => false, ); $args = array_merge($defaultArgs, $args); @@ -67,6 +68,7 @@ public function getSubmissionList($contextId, $args = array()) { ->filterByStatus($args['status']) ->filterByStageIds($args['stageIds']) ->filterByIncomplete($args['isIncomplete']) + ->filterByOverdue($args['isOverdue']) ->searchPhrase($args['searchPhrase']); \HookRegistry::call('Submission::getSubmissionList::queryBuilder', array(&$submissionListQB, $contextId, $args)); @@ -511,7 +513,7 @@ public function toArrayStageDetails($submission, $stageIds = null) { $reviewRoundDao = DAORegistry::getDAO('ReviewRoundDAO'); $reviewRound = $reviewRoundDao->getLastReviewRoundBySubmissionId($submission->getId(), $stageId); if ($reviewRound) { - $stage['statusId'] = $reviewRound->getStatus(); + $stage['statusId'] = $reviewRound->determineStatus(); $stage['status'] = __($reviewRound->getStatusKey()); // Revision files in this round. @@ -565,7 +567,7 @@ public function toArrayReviewRounds($submission) { 'id' => $reviewRound->getId(), 'round' => $reviewRound->getRound(), 'stageId' => $reviewRound->getStageId(), - 'statusId' => $reviewRound->getStatus(), + 'statusId' => $reviewRound->determineStatus(), 'status' => __($reviewRound->getStatusKey()), ); } diff --git a/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php b/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php index d3ca1809554..35982e40caf 100644 --- a/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php +++ b/classes/services/queryBuilders/PKPSubmissionListQueryBuilder.inc.php @@ -49,6 +49,9 @@ abstract class PKPSubmissionListQueryBuilder extends BaseQueryBuilder { /** @var bool whether to return only incomplete results */ protected $isIncomplete = false; + /** @var bool whether to return only submissions with overdue review assignments */ + protected $isOverdue = false; + /** * Constructor * @@ -121,6 +124,18 @@ public function filterByIncomplete($isIncomplete) { return $this; } + /** + * Set overdue submissions filter + * + * @param boolean $isOverdue + * + * @return \OJS\Services\QueryBuilders\SubmissionListQueryBuilder + */ + public function filterByOverdue($isOverdue) { + $this->isOverdue = $isOverdue; + return $this; + } + /** * Limit results to a specific user's submissions * @@ -196,6 +211,23 @@ public function get() { $q->where('s.submission_progress', '>', 0); } + // overdue submisions + if ($this->isOverdue) { + $q->leftJoin('review_assignments as raod', 'raod.submission_id', '=', 's.submission_id') + ->leftJoin('review_rounds as rr', function($table) { + $table->on('rr.submission_id', '=', 's.submission_id'); + $table->on('raod.review_round_id', '=', 'rr.review_round_id'); + }); + // Only get overdue assignments on active review rounds + import('lib.pkp.classes.submission.reviewRound.ReviewRound'); + $q->where('rr.status', '!=', REVIEW_ROUND_STATUS_RESUBMITTED); + $q->where('rr.status', '!=', REVIEW_ROUND_STATUS_SENT_TO_EXTERNAL); + $q->where('rr.status', '!=', REVIEW_ROUND_STATUS_ACCEPTED); + $q->where('rr.status', '!=', REVIEW_ROUND_STATUS_DECLINED); + $q->where('raod.date_due', '<', \Core::getCurrentDate(strtotime('tomorrow'))); + $q->where('raod.date_response_due', '<', \Core::getCurrentDate(strtotime('tomorrow'))); + } + // assigned to if (!is_null($this->assigneeId) && ($this->assigneeId !== -1)) { $assigneeId = $this->assigneeId; diff --git a/classes/submission/reviewRound/ReviewRound.inc.php b/classes/submission/reviewRound/ReviewRound.inc.php index ec310472c04..6d98ad5e033 100644 --- a/classes/submission/reviewRound/ReviewRound.inc.php +++ b/classes/submission/reviewRound/ReviewRound.inc.php @@ -201,7 +201,7 @@ public function determineStatus() { * @return int */ function getStatusKey($isAuthor = false) { - switch ($this->getStatus()) { + switch ($this->determineStatus()) { case REVIEW_ROUND_STATUS_REVISIONS_REQUESTED: return 'editor.submission.roundStatus.revisionsRequested'; case REVIEW_ROUND_STATUS_REVISIONS_SUBMITTED: diff --git a/controllers/list/submissions/PKPSubmissionsListHandler.inc.php b/controllers/list/submissions/PKPSubmissionsListHandler.inc.php index f59784ed247..2d45591e9ab 100644 --- a/controllers/list/submissions/PKPSubmissionsListHandler.inc.php +++ b/controllers/list/submissions/PKPSubmissionsListHandler.inc.php @@ -98,18 +98,24 @@ public function getConfig() { $config['getParams'] = $this->_getParams; $config['filters'] = array( - 'stageIds' => array( - 'heading' => __('settings.roles.stages'), - 'filters' => $this->getWorkflowStages(), - ), - 'isIncomplete' => array( + 'attention' => array( 'filters' => array( array( + 'param' => 'isOverdue', + 'val' => true, + 'title' => __('common.overdue'), + ), + array( + 'param' => 'isIncomplete', 'val' => true, 'title' => __('submissions.incomplete'), ), ), ), + 'stageIds' => array( + 'heading' => __('settings.roles.stages'), + 'filters' => $this->getWorkflowStages(), + ), ); // Load grid localisation files diff --git a/js/controllers/list/submissions/SubmissionsListFilter.vue b/js/controllers/list/submissions/SubmissionsListFilter.vue index d67a493724e..c2daa603742 100644 --- a/js/controllers/list/submissions/SubmissionsListFilter.vue +++ b/js/controllers/list/submissions/SubmissionsListFilter.vue @@ -5,23 +5,23 @@ {{ i18n.filter }}
-
+
{{ filter.heading }}
  • {{ filterItem.title }}