mirror of
https://github.com/moodle/moodle.git
synced 2025-04-14 04:52:36 +02:00
Merge branch 'MDL-72662' of git://github.com/paulholden/moodle
This commit is contained in:
commit
af94d5b09c
@ -281,7 +281,13 @@ class task_log extends base {
|
||||
$this->get_entity_name(),
|
||||
"{$tablealias}.timestart"
|
||||
))
|
||||
->add_joins($this->get_joins());
|
||||
->add_joins($this->get_joins())
|
||||
->set_limited_operators([
|
||||
date::DATE_ANY,
|
||||
date::DATE_RANGE,
|
||||
date::DATE_PREVIOUS,
|
||||
date::DATE_CURRENT,
|
||||
]);
|
||||
|
||||
// Duration filter.
|
||||
$filters[] = (new filter(
|
||||
|
@ -34,8 +34,15 @@ $string['errorreportaccess'] = 'You can not view this report';
|
||||
$string['errorsourceinvalid'] = 'Could not find valid report source';
|
||||
$string['errorsourceunavailable'] = 'Report source is not available';
|
||||
$string['filtercontains'] = 'Contains';
|
||||
$string['filterdatecurrent'] = 'Current';
|
||||
$string['filterdatedays'] = 'Day(s)';
|
||||
$string['filterdatefrom'] = 'Date from';
|
||||
$string['filterdatemonths'] = 'Month(s)';
|
||||
$string['filterdatenext'] = 'Next';
|
||||
$string['filterdateprevious'] = 'Previous';
|
||||
$string['filterdateto'] = 'Date to';
|
||||
$string['filterdateweeks'] = 'Week(s)';
|
||||
$string['filterdateyears'] = 'Year(s)';
|
||||
$string['filterdoesnotcontain'] = 'Does not contain';
|
||||
$string['filterdurationunit'] = '{$a} unit';
|
||||
$string['filterendswith'] = 'Ends with';
|
||||
|
@ -154,8 +154,6 @@ class config_change extends base {
|
||||
* @return filter[]
|
||||
*/
|
||||
protected function get_all_filters(): array {
|
||||
global $DB;
|
||||
|
||||
$tablealias = $this->get_table_alias('config_log');
|
||||
|
||||
// Time modified filter.
|
||||
@ -170,6 +168,8 @@ class config_change extends base {
|
||||
->set_limited_operators([
|
||||
date::DATE_ANY,
|
||||
date::DATE_RANGE,
|
||||
date::DATE_PREVIOUS,
|
||||
date::DATE_CURRENT,
|
||||
]);
|
||||
|
||||
// Setting filter.
|
||||
|
@ -18,6 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace core_reportbuilder\local\filters;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use lang_string;
|
||||
use MoodleQuickForm;
|
||||
use core_reportbuilder\local\helpers\database;
|
||||
@ -45,6 +46,27 @@ class date extends base {
|
||||
/** @var int Date within defined range */
|
||||
public const DATE_RANGE = 3;
|
||||
|
||||
/** @var int Date in the previous [X relative date unit(s)] */
|
||||
public const DATE_PREVIOUS = 4;
|
||||
|
||||
/** @var int Date in current [relative date unit] */
|
||||
public const DATE_CURRENT = 5;
|
||||
|
||||
/** @var int Date in the next [X relative date unit(s)] */
|
||||
public const DATE_NEXT = 6;
|
||||
|
||||
/** @var int Relative date unit for a day */
|
||||
public const DATE_UNIT_DAY = 1;
|
||||
|
||||
/** @var int Relative date unit for a week */
|
||||
public const DATE_UNIT_WEEK = 2;
|
||||
|
||||
/** @var int Relative date unit for a month */
|
||||
public const DATE_UNIT_MONTH = 3;
|
||||
|
||||
/** @var int Relative date unit for a month */
|
||||
public const DATE_UNIT_YEAR = 4;
|
||||
|
||||
/**
|
||||
* Return an array of operators available for this filter
|
||||
*
|
||||
@ -56,6 +78,9 @@ class date extends base {
|
||||
self::DATE_NOT_EMPTY => new lang_string('filterisnotempty', 'core_reportbuilder'),
|
||||
self::DATE_EMPTY => new lang_string('filterisempty', 'core_reportbuilder'),
|
||||
self::DATE_RANGE => new lang_string('filterrange', 'core_reportbuilder'),
|
||||
self::DATE_PREVIOUS => new lang_string('filterdateprevious', 'core_reportbuilder'),
|
||||
self::DATE_CURRENT => new lang_string('filterdatecurrent', 'core_reportbuilder'),
|
||||
self::DATE_NEXT => new lang_string('filterdatenext', 'core_reportbuilder'),
|
||||
];
|
||||
|
||||
return $this->filter->restrict_limited_operators($operators);
|
||||
@ -67,11 +92,46 @@ class date extends base {
|
||||
* @param MoodleQuickForm $mform
|
||||
*/
|
||||
public function setup_form(MoodleQuickForm $mform): void {
|
||||
// Operator selector.
|
||||
$operatorlabel = get_string('filterfieldoperator', 'core_reportbuilder', $this->get_header());
|
||||
$mform->addElement('select', "{$this->name}_operator", $operatorlabel, $this->get_operators())->setHiddenLabel(true);
|
||||
|
||||
$elements[] = $mform->createElement('select', "{$this->name}_operator", $operatorlabel, $this->get_operators());
|
||||
$mform->setType("{$this->name}_operator", PARAM_INT);
|
||||
$mform->setDefault("{$this->name}_operator", self::DATE_ANY);
|
||||
|
||||
// Value selector for previous and next operators.
|
||||
$valuelabel = get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header());
|
||||
|
||||
$elements[] = $mform->createElement('text', "{$this->name}_value", $valuelabel, ['size' => 3]);
|
||||
$mform->setType("{$this->name}_value", PARAM_INT);
|
||||
$mform->setDefault("{$this->name}_value", 1);
|
||||
$mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'eq', self::DATE_ANY);
|
||||
$mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'eq', self::DATE_NOT_EMPTY);
|
||||
$mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'eq', self::DATE_EMPTY);
|
||||
$mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'eq', self::DATE_RANGE);
|
||||
$mform->disabledIf("{$this->name}_value", "{$this->name}_operator", 'eq', self::DATE_CURRENT);
|
||||
|
||||
// Unit selector for previous and next operators.
|
||||
$unitlabel = get_string('filterdurationunit', 'core_reportbuilder', $this->get_header());
|
||||
$units = [
|
||||
self::DATE_UNIT_DAY => get_string('filterdatedays', 'core_reportbuilder'),
|
||||
self::DATE_UNIT_WEEK => get_string('filterdateweeks', 'core_reportbuilder'),
|
||||
self::DATE_UNIT_MONTH => get_string('filterdatemonths', 'core_reportbuilder'),
|
||||
self::DATE_UNIT_YEAR => get_string('filterdateyears', 'core_reportbuilder'),
|
||||
];
|
||||
|
||||
$elements[] = $mform->createElement('select', "{$this->name}_unit", $unitlabel, $units);
|
||||
$mform->setType("{$this->name}_unit", PARAM_INT);
|
||||
$mform->setDefault("{$this->name}_unit", self::DATE_UNIT_DAY);
|
||||
$mform->hideIf("{$this->name}_unit", "{$this->name}_operator", 'eq', self::DATE_ANY);
|
||||
$mform->hideIf("{$this->name}_unit", "{$this->name}_operator", 'eq', self::DATE_NOT_EMPTY);
|
||||
$mform->hideIf("{$this->name}_unit", "{$this->name}_operator", 'eq', self::DATE_EMPTY);
|
||||
$mform->hideIf("{$this->name}_unit", "{$this->name}_operator", 'eq', self::DATE_RANGE);
|
||||
|
||||
// Add operator/value/unit group.
|
||||
$mform->addGroup($elements, "{$this->name}_group", '', null, false);
|
||||
|
||||
// Date selectors for range operator.
|
||||
$mform->addElement('date_selector', "{$this->name}_from", get_string('filterdatefrom', 'core_reportbuilder'),
|
||||
['optional' => true]);
|
||||
$mform->setType("{$this->name}_from", PARAM_INT);
|
||||
@ -95,7 +155,10 @@ class date extends base {
|
||||
$fieldsql = $this->filter->get_field_sql();
|
||||
$params = $this->filter->get_field_params();
|
||||
|
||||
$operator = $values["{$this->name}_operator"] ?? self::DATE_ANY;
|
||||
$operator = (int) ($values["{$this->name}_operator"] ?? self::DATE_ANY);
|
||||
$dateunitvalue = (int) ($values["{$this->name}_value"] ?? 1);
|
||||
$dateunit = (int) ($values["{$this->name}_unit"] ?? self::DATE_UNIT_DAY);
|
||||
|
||||
switch ($operator) {
|
||||
case self::DATE_NOT_EMPTY:
|
||||
$sql = "{$fieldsql} IS NOT NULL AND {$fieldsql} <> 0";
|
||||
@ -122,6 +185,26 @@ class date extends base {
|
||||
|
||||
$sql = implode(' AND ', $clauses);
|
||||
|
||||
break;
|
||||
// Relative helper method can handle these three cases.
|
||||
case self::DATE_PREVIOUS:
|
||||
case self::DATE_CURRENT:
|
||||
case self::DATE_NEXT:
|
||||
|
||||
// Previous and next operators require a unit value greater than zero.
|
||||
if ($operator !== self::DATE_CURRENT && $dateunitvalue === 0) {
|
||||
return ['', []];
|
||||
}
|
||||
|
||||
$paramdatefrom = database::generate_param_name();
|
||||
$paramdateto = database::generate_param_name();
|
||||
|
||||
$sql = "{$fieldsql} >= :{$paramdatefrom} AND {$fieldsql} <= :{$paramdateto}";
|
||||
[
|
||||
$params[$paramdatefrom],
|
||||
$params[$paramdateto],
|
||||
] = self::get_relative_timeframe($operator, $dateunitvalue, $dateunit);
|
||||
|
||||
break;
|
||||
default:
|
||||
// Invalid or inactive filter.
|
||||
@ -130,4 +213,84 @@ class date extends base {
|
||||
|
||||
return [$sql, $params];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return start and end time of given relative date period
|
||||
*
|
||||
* @param int $operator
|
||||
* @param int $dateunitvalue
|
||||
* @param int $dateunit
|
||||
* @return int[]
|
||||
*/
|
||||
private static function get_relative_timeframe(int $operator, int $dateunitvalue, int $dateunit): array {
|
||||
$datenow = new DateTimeImmutable();
|
||||
|
||||
switch ($dateunit) {
|
||||
case self::DATE_UNIT_DAY:
|
||||
// Current day.
|
||||
$datestart = $dateend = $datenow;
|
||||
|
||||
if ($operator === self::DATE_PREVIOUS) {
|
||||
$datestart = $datestart->modify("-{$dateunitvalue} day");
|
||||
$dateend = $dateend->modify('-1 day');
|
||||
} else if ($operator === self::DATE_NEXT) {
|
||||
$datestart = $datestart->modify('+1 day');
|
||||
$dateend = $dateend->modify("+{$dateunitvalue} day");
|
||||
}
|
||||
|
||||
break;
|
||||
case self::DATE_UNIT_WEEK:
|
||||
// Current week.
|
||||
$datestart = $datenow->modify('monday this week');
|
||||
$dateend = $datenow->modify('sunday this week');
|
||||
|
||||
if ($operator === self::DATE_PREVIOUS) {
|
||||
$datestart = $datestart->modify("-{$dateunitvalue} week");
|
||||
$dateend = $dateend->modify('-1 week');
|
||||
} else if ($operator === self::DATE_NEXT) {
|
||||
$datestart = $datestart->modify('+1 week');
|
||||
$dateend = $dateend->modify("+{$dateunitvalue} week");
|
||||
}
|
||||
|
||||
break;
|
||||
case self::DATE_UNIT_MONTH:
|
||||
// Current month.
|
||||
$datestart = $datenow->modify('first day of this month');
|
||||
$dateend = $datenow->modify('last day of this month');
|
||||
|
||||
[$dateyear, $datemonth] = explode('/', $datenow->format('Y/m'));
|
||||
if ($operator === self::DATE_PREVIOUS) {
|
||||
$datestart = $datestart->setDate((int) $dateyear, $datemonth - $dateunitvalue, 1);
|
||||
$dateend = $dateend->modify('last day of last month');
|
||||
} else if ($operator === self::DATE_NEXT) {
|
||||
$datestart = $datestart->modify('first day of next month');
|
||||
$dateend = $dateend->setDate((int) $dateyear, $datemonth + $dateunitvalue, 1)
|
||||
->modify('last day of this month');
|
||||
}
|
||||
|
||||
break;
|
||||
case self::DATE_UNIT_YEAR:
|
||||
// Current year.
|
||||
$datestart = $datenow->modify('first day of january this year');
|
||||
$dateend = $datenow->modify('last day of december this year');
|
||||
|
||||
$dateyear = (int) $datenow->format('Y');
|
||||
if ($operator === self::DATE_PREVIOUS) {
|
||||
$datestart = $datestart->setDate($dateyear - $dateunitvalue, 1, 1);
|
||||
$dateend = $dateend->modify('last day of december last year');
|
||||
} else if ($operator === self::DATE_NEXT) {
|
||||
$datestart = $datestart->modify('first day of january next year');
|
||||
$dateend = $dateend->setDate($dateyear + $dateunitvalue, 12, 31);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
return [
|
||||
$datestart->setTime(0, 0)->getTimestamp(),
|
||||
$dateend->setTime(23, 59, 59)->getTimestamp(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ use core_reportbuilder\local\report\filter;
|
||||
* @copyright 2021 Paul Holden <paulh@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class date_testcase extends advanced_testcase {
|
||||
class date_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Data provider for {@see test_get_sql_filter_simple}
|
||||
@ -114,4 +114,64 @@ class date_testcase extends advanced_testcase {
|
||||
$usernames = $DB->get_fieldset_select('user', 'username', $select, $params);
|
||||
$this->assertEquals([$usertwo->username], $usernames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for {@see test_get_sql_filter_relative}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_sql_filter_relative_provider(): array {
|
||||
return [
|
||||
'Previous day' => [date::DATE_PREVIOUS, 1, date::DATE_UNIT_DAY, '-1 day'],
|
||||
'Previous week' => [date::DATE_PREVIOUS, 1, date::DATE_UNIT_WEEK, '-1 week'],
|
||||
'Previous month' => [date::DATE_PREVIOUS, 1, date::DATE_UNIT_MONTH, 'last day of last month'],
|
||||
'Previous year' => [date::DATE_PREVIOUS, 1, date::DATE_UNIT_YEAR, 'last day of december last year'],
|
||||
|
||||
'Current day' => [date::DATE_CURRENT, null, date::DATE_UNIT_DAY],
|
||||
'Current week' => [date::DATE_CURRENT, null, date::DATE_UNIT_WEEK],
|
||||
'Current month' => [date::DATE_CURRENT, null, date::DATE_UNIT_MONTH],
|
||||
'Current year' => [date::DATE_CURRENT, null, date::DATE_UNIT_YEAR],
|
||||
|
||||
'Next day' => [date::DATE_NEXT, 1, date::DATE_UNIT_DAY, '+1 day'],
|
||||
'Next week' => [date::DATE_NEXT, 1, date::DATE_UNIT_WEEK, '+1 week'],
|
||||
'Next month' => [date::DATE_NEXT, 1, date::DATE_UNIT_MONTH, 'first day of next month'],
|
||||
'Next year' => [date::DATE_NEXT, 1, date::DATE_UNIT_YEAR, 'first day of january next year'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit tests for filtering relative dates
|
||||
*
|
||||
* @param int $operator
|
||||
* @param int|null $unitvalue
|
||||
* @param int $unit
|
||||
* @param string|null $timecreated Relative time suitable for passing to {@see strtotime} (or null for current time)
|
||||
*
|
||||
* @dataProvider get_sql_filter_relative_provider
|
||||
*/
|
||||
public function test_get_sql_filter_relative(int $operator, ?int $unitvalue, int $unit, ?string $timecreated = null): void {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$usertimecreated = ($timecreated !== null ? strtotime($timecreated) : time());
|
||||
$user = $this->getDataGenerator()->create_user(['timecreated' => $usertimecreated]);
|
||||
|
||||
$filter = new filter(
|
||||
date::class,
|
||||
'test',
|
||||
new lang_string('yes'),
|
||||
'testentity',
|
||||
'timecreated'
|
||||
);
|
||||
|
||||
[$select, $params] = date::create($filter)->get_sql_filter([
|
||||
$filter->get_unique_identifier() . '_operator' => $operator,
|
||||
$filter->get_unique_identifier() . '_value' => $unitvalue,
|
||||
$filter->get_unique_identifier() . '_unit' => $unit,
|
||||
]);
|
||||
|
||||
$matchingusers = $DB->get_fieldset_select('user', 'username', $select, $params);
|
||||
$this->assertContains($user->username, $matchingusers);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user