MDL-83862 qbank_viewcreator: Add timemodified filter
This adds a new filter for the timemodified, displayed in the "Modified by" column. It uses the new core datetime filter for the UI.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
namespace qbank_viewcreator;
|
||||
|
||||
use core_question\local\bank\plugin_features_base;
|
||||
use core_question\local\bank\view;
|
||||
|
||||
/**
|
||||
* Class plugin_feature is the entrypoint for the columns.
|
||||
@@ -27,11 +28,18 @@ use core_question\local\bank\plugin_features_base;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class plugin_feature extends plugin_features_base {
|
||||
|
||||
#[\Override]
|
||||
public function get_question_columns($qbank): array {
|
||||
return [
|
||||
new creator_name_column($qbank),
|
||||
new modifier_name_column($qbank),
|
||||
];
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function get_question_filters(?view $qbank = null): array {
|
||||
return [
|
||||
new timemodified_condition($qbank),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace qbank_viewcreator;
|
||||
|
||||
use core\exception\moodle_exception;
|
||||
use core\plugininfo\filter;
|
||||
use core_question\local\bank\condition;
|
||||
|
||||
/**
|
||||
* Filter condition for date/time modified
|
||||
*
|
||||
* @package qbank_viewcreator
|
||||
* @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class timemodified_condition extends condition {
|
||||
/**
|
||||
* @var string Search for times before the specified date.
|
||||
*/
|
||||
const MODE_BEFORE = 'before';
|
||||
|
||||
/**
|
||||
* @var string Search for times after the specified date.
|
||||
*/
|
||||
const MODE_AFTER = 'after';
|
||||
|
||||
/**
|
||||
* @var string Search for times between the specified dates.
|
||||
*/
|
||||
const MODE_BETWEEN = 'between';
|
||||
|
||||
#[\Override]
|
||||
public function get_title() {
|
||||
return get_string('timemodified', 'qbank_viewcreator');
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public static function get_condition_key() {
|
||||
return 'timemodified';
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function get_filter_class() {
|
||||
return 'core/datafilter/filtertypes/datetime';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a single valid jointype, so we don't display the jointype selector.
|
||||
*
|
||||
* We have a separate filter option to control how this condition is applied, Any/All/None doesn't apply here.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_join_list(): array {
|
||||
return [
|
||||
self::JOINTYPE_DEFAULT,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an SQL WHERE condition to filter questions based on q.timemodified.
|
||||
*
|
||||
* $filter['values'][0] contains the datetime to search after, $filter['values'][1] contains the datetime
|
||||
* to search before. Whether to use these dates to search after, before, or between these dates is determined
|
||||
* by the value of $filter['fileroptions']['mode'].
|
||||
*
|
||||
* The datetime values are in the format YYYY-MM-DDTHH:mm, as provided by the datetime-local input type.
|
||||
*
|
||||
* @param array $filter ['values' => [$before, $after], 'filteroptions' => ['mode' => $mode]]
|
||||
* @return array
|
||||
* @throws moodle_exception If an invalid mode or range is provided.
|
||||
*/
|
||||
public static function build_query_from_filter(array $filter): array {
|
||||
if (!isset($filter['filteroptions']['mode']) || empty($filter['values'])) {
|
||||
return ['', []];
|
||||
}
|
||||
$mode = $filter['filteroptions']['mode'];
|
||||
if (!in_array($mode, [self::MODE_AFTER, self::MODE_BEFORE, self::MODE_BETWEEN])) {
|
||||
throw new moodle_exception('invaliddatetimemode', 'error', a: $filter['filteroptions']['mode']);
|
||||
}
|
||||
$tz = new \DateTimeZone(\core_date::get_user_timezone(99));
|
||||
$datetimeafter = new \DateTime($filter['values'][0], $tz);
|
||||
$datetimebefore = new \DateTime($filter['values'][1], $tz);
|
||||
if ($mode === self::MODE_AFTER) {
|
||||
$conditions = 'q.timemodified > :timemodifiedafter';
|
||||
$params['timemodifiedafter'] = $datetimeafter->getTimestamp();
|
||||
} else if ($mode === self::MODE_BEFORE) {
|
||||
$conditions = 'q.timemodified < :timemodifiedbefore';
|
||||
$params['timemodifiedbefore'] = $datetimebefore->getTimestamp();
|
||||
} else {
|
||||
if ($datetimeafter > $datetimebefore) {
|
||||
throw new moodle_exception(
|
||||
'invaliddatetimebetween',
|
||||
'error',
|
||||
a: (object) [
|
||||
'before' => $datetimebefore->format('Y-m-d H:i'),
|
||||
'after' => $datetimeafter->format('Y-m-d H:i'),
|
||||
],
|
||||
);
|
||||
}
|
||||
$conditions = 'q.timemodified > :timemodifiedafter AND q.timemodified < :timemodifiedbefore';
|
||||
$params = [
|
||||
'timemodifiedafter' => $datetimeafter->getTimestamp(),
|
||||
'timemodifiedbefore' => $datetimebefore->getTimestamp(),
|
||||
];
|
||||
}
|
||||
|
||||
return [$conditions, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default datetime values for the filter.
|
||||
*
|
||||
* This generates values formatted for datetime-local fields. The first value returned is the current time,
|
||||
* for use as the default "before" datetime. The second is midnight 1 week ago, for use as the default "after"
|
||||
* datetime.
|
||||
*
|
||||
* @return array[]
|
||||
*/
|
||||
public function get_initial_values(): array {
|
||||
$tz = new \DateTimeZone(\core_date::get_user_timezone());
|
||||
// Datetime format used by the <input type="datetime-local"> field.
|
||||
$format = 'Y-m-d\TH:i';
|
||||
$now = (new \DateTime('now', $tz))->format($format);
|
||||
$oneweek = (new \DateTime('midnight 1 week ago', $tz))->format($format);
|
||||
return [
|
||||
[
|
||||
'value' => $now,
|
||||
'title' => $now,
|
||||
],
|
||||
[
|
||||
'value' => $oneweek,
|
||||
'title' => $oneweek,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
$string['modifiedby'] = 'Modified by';
|
||||
$string['timemodified'] = 'Time modified';
|
||||
$string['pluginname'] = 'View creator';
|
||||
$string['privacy:metadata'] = 'The View creator question bank plugin does not store any personal data.';
|
||||
$string['version'] = 'Version {$a}';
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
@qbank @qbank_viewcreator @javascript
|
||||
Feature: Time modified filter condition
|
||||
As a teacher
|
||||
In order to organise my questions
|
||||
I want to filter the list of questions by the time and date of last modification
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| qbank | Qbank 1 | Question bank 1 | C1 | qbank1 |
|
||||
And the following "question categories" exist:
|
||||
| contextlevel | reference | name |
|
||||
| Activity module | qbank1 | Test questions |
|
||||
And the following "questions" exist:
|
||||
| questioncategory | qtype | name | questiontext |
|
||||
| Test questions | truefalse | First question | Answer the first question |
|
||||
| Test questions | truefalse | Second question | Answer the second question |
|
||||
| Test questions | truefalse | Third question | Answer the third question |
|
||||
And the following "core_question > updated questions" exist:
|
||||
| questioncategory | question | name | timemodified |
|
||||
| Test questions | First question | First question | ## 2024-01-10 10:00 ## |
|
||||
| Test questions | Second question | Second question | ## 2024-01-10 11:00 ## |
|
||||
| Test questions | Third question | Third question | ## 2024-01-10 12:00 ## |
|
||||
Given I am on the "Qbank 1" "core_question > question bank" page logged in as "admin"
|
||||
And I should see "First question"
|
||||
And I should see "Second question"
|
||||
And I should see "Third question"
|
||||
|
||||
Scenario: Filter by questions modified before time
|
||||
When I add question bank filter "Time modified"
|
||||
And the field "Time modified before" matches value "## now ##%FT%R##"
|
||||
And I set the field "Time modified before" to "2024-01-10T10:59"
|
||||
And I press "Apply filters"
|
||||
Then I should see "First question"
|
||||
And I should not see "Second question"
|
||||
And I should not see "Third question"
|
||||
|
||||
Scenario: Filter by questions modified after time
|
||||
When I add question bank filter "Time modified"
|
||||
And I set the field "Select dates" to "After"
|
||||
And the field "Time modified after" matches value "## midnight 1 week ago ##%FT%R##"
|
||||
And I set the field "Time modified after" to "2024-01-10T10:59"
|
||||
And I press "Apply filters"
|
||||
Then I should not see "First question"
|
||||
And I should see "Second question"
|
||||
And I should see "Third question"
|
||||
|
||||
Scenario: Filter by questions modified between times
|
||||
When I add question bank filter "Time modified"
|
||||
And I set the field "Select dates" to "Between"
|
||||
And I set the field "Time modified after" to "2024-01-10T10:59"
|
||||
And I set the field "Time modified before" to "2024-01-10T11:01"
|
||||
And I press "Apply filters"
|
||||
Then I should not see "First question"
|
||||
And I should see "Second question"
|
||||
And I should not see "Third question"
|
||||
|
||||
Scenario: Apply filter between invalid times
|
||||
When I add question bank filter "Time modified"
|
||||
And I set the field "Select dates" to "Between"
|
||||
And I set the field "Time modified after" to "2024-01-10T10:59"
|
||||
And I set the field "Time modified before" to "2024-01-10T11:01"
|
||||
And I press "Apply filters"
|
||||
And I should not see "First question"
|
||||
And I should see "Second question"
|
||||
And I should not see "Third question"
|
||||
And I set the field "Time modified after" to "2024-01-10T11:01"
|
||||
And I set the field "Time modified before" to "2024-01-10T10:59"
|
||||
And I press "Apply filters"
|
||||
# Invalid filters should not be applied.
|
||||
And I should not see "First question"
|
||||
And I should see "Second question"
|
||||
And I should not see "Third question"
|
||||
# Invalid values should not be set in the URL.
|
||||
And I reload the page
|
||||
And the field "Time modified after" matches value "2024-01-10T10:59"
|
||||
And the field "Time modified before" matches value "2024-01-10T11:01"
|
||||
And I should not see "First question"
|
||||
And I should see "Second question"
|
||||
And I should not see "Third question"
|
||||
Reference in New Issue
Block a user