diff --git a/enrol/tests/enrollib_test.php b/enrol/tests/enrollib_test.php index acd3e6086f1..0d4447128b6 100644 --- a/enrol/tests/enrollib_test.php +++ b/enrol/tests/enrollib_test.php @@ -1362,4 +1362,77 @@ class core_enrollib_testcase extends advanced_testcase { $durationinday = $duration / DAYSECS; $this->assertEquals(9, $durationinday); } + + /** + * Test get_enrolled_with_capabilities_join cannotmatchanyrows attribute. + * + * @dataProvider get_enrolled_with_capabilities_join_cannotmatchanyrows_data() + * @param string $capability the tested capability + * @param bool $useprohibit if the capability must be assigned to prohibit + * @param int $expectedmatch expected cannotmatchanyrows value + * @param int $expectedcount expceted count value + */ + public function test_get_enrolled_with_capabilities_join_cannotmatchanyrows( + string $capability, + bool $useprohibit, + int $expectedmatch, + int $expectedcount + ) { + global $DB, $CFG; + + $this->resetAfterTest(); + + $course = $this->getDataGenerator()->create_course(); + $context = context_course::instance($course->id); + + $roleid = $CFG->defaultuserroleid; + + // Override capability if necessary. + if ($useprohibit && $capability) { + assign_capability($capability, CAP_PROHIBIT, $roleid, $context); + } + + // Check if we must enrol or not. + $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); + + $join = get_enrolled_with_capabilities_join($context, '', $capability); + + // Execute query. + $sql = "SELECT COUNT(DISTINCT u.id) + FROM {user} u {$join->joins} + WHERE {$join->wheres}"; + $countrecords = $DB->count_records_sql($sql, $join->params); + + // Validate cannotmatchanyrows. + $this->assertEquals($expectedmatch, $join->cannotmatchanyrows); + $this->assertEquals($expectedcount, $countrecords); + } + + /** + * Data provider for test_get_enrolled_with_capabilities_join_cannotmatchanyrows + * + * @return @array of testing scenarios + */ + public function get_enrolled_with_capabilities_join_cannotmatchanyrows_data() { + return [ + 'no prohibits, no capability' => [ + 'capability' => '', + 'useprohibit' => false, + 'expectedmatch' => 0, + 'expectedcount' => 1, + ], + 'no prohibits with capability' => [ + 'capability' => 'moodle/course:manageactivities', + 'useprohibit' => false, + 'expectedmatch' => 0, + 'expectedcount' => 1, + ], + 'prohibits with capability' => [ + 'capability' => 'moodle/course:manageactivities', + 'useprohibit' => true, + 'expectedmatch' => 1, + 'expectedcount' => 0, + ], + ]; + } } diff --git a/lib/enrollib.php b/lib/enrollib.php index 7b40ca068ce..22611c6af44 100644 --- a/lib/enrollib.php +++ b/lib/enrollib.php @@ -1384,6 +1384,10 @@ function is_enrolled(context $context, $user = null, $withcapability = '', $only * several times (e.g. as manual enrolment, and as self enrolment). You may * need to use a SELECT DISTINCT in your query (see get_enrolled_sql for example). * + * In case is guaranteed some of the joins never match any rows, the resulting + * join_sql->cannotmatchanyrows will be true. This happens when the capability + * is prohibited. + * * @param context $context * @param string $prefix optional, a prefix to the user id column * @param string|array $capability optional, may include a capability name, or array of names. @@ -1393,24 +1397,27 @@ function is_enrolled(context $context, $user = null, $withcapability = '', $only * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions * @param bool $onlysuspended inverse of onlyactive, consider only suspended enrolments * @param int $enrolid The enrolment ID. If not 0, only users enrolled using this enrolment method will be returned. - * @return \core\dml\sql_join Contains joins, wheres, params + * @return \core\dml\sql_join Contains joins, wheres, params and cannotmatchanyrows */ function get_enrolled_with_capabilities_join(context $context, $prefix = '', $capability = '', $group = 0, $onlyactive = false, $onlysuspended = false, $enrolid = 0) { $uid = $prefix . 'u.id'; $joins = array(); $wheres = array(); + $cannotmatchanyrows = false; $enrolledjoin = get_enrolled_join($context, $uid, $onlyactive, $onlysuspended, $enrolid); $joins[] = $enrolledjoin->joins; $wheres[] = $enrolledjoin->wheres; $params = $enrolledjoin->params; + $cannotmatchanyrows = $cannotmatchanyrows || $enrolledjoin->cannotmatchanyrows; if (!empty($capability)) { $capjoin = get_with_capability_join($context, $capability, $uid); $joins[] = $capjoin->joins; $wheres[] = $capjoin->wheres; $params = array_merge($params, $capjoin->params); + $cannotmatchanyrows = $cannotmatchanyrows || $capjoin->cannotmatchanyrows; } if ($group) { @@ -1420,13 +1427,14 @@ function get_enrolled_with_capabilities_join(context $context, $prefix = '', $ca if (!empty($groupjoin->wheres)) { $wheres[] = $groupjoin->wheres; } + $cannotmatchanyrows = $cannotmatchanyrows || $groupjoin->cannotmatchanyrows; } $joins = implode("\n", $joins); $wheres[] = "{$prefix}u.deleted = 0"; $wheres = implode(" AND ", $wheres); - return new \core\dml\sql_join($joins, $wheres, $params); + return new \core\dml\sql_join($joins, $wheres, $params, $cannotmatchanyrows); } /**