diff --git a/lib/upgrade.txt b/lib/upgrade.txt index 45527557674..7218fab4496 100644 --- a/lib/upgrade.txt +++ b/lib/upgrade.txt @@ -11,6 +11,10 @@ attribute on forms to avoid collisions in forms loaded in AJAX requests. to be updated or queued as required. This new functionality can be found in \core\task\manager::reschedule_or_queue_adhoc_task. * Icons are displayed for screen readers unless they have empty alt text (aria-hidden). Do not provide an icon with alt text immediately beside an element with exactly the same text. * admin_settingpage has a new function hide_if(), modeled after the same functionality in the forms library. This allows admin settings to be dynamically hidden based on the values of other settings. +* The \core_rating provider's get_sql_join function now accepts an optional $innerjoin parameter. + It is recommended that privacy providers using this function call rewrite any long query into a number of separate + calls to add_from_sql for improved performance, and that the new argument is used. + This will allow queries to remain backwards-compatible with older versions of Moodle but will have significantly better performance in version supporting the innerjoin parameter. === 3.6 === diff --git a/mod/data/classes/privacy/provider.php b/mod/data/classes/privacy/provider.php index b80cfffbcf2..43898d0e87e 100644 --- a/mod/data/classes/privacy/provider.php +++ b/mod/data/classes/privacy/provider.php @@ -103,29 +103,57 @@ class provider implements * @return contextlist the list of contexts containing user info for the user. */ public static function get_contexts_for_userid(int $userid) : contextlist { - // Fetch all data records. + $contextlist = new contextlist(); + + // Fetch all data records that the user rote. $sql = "SELECT c.id FROM {context} c - INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel - INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname - INNER JOIN {data} d ON d.id = cm.instance - INNER JOIN {data_records} dr ON dr.dataid = d.id - LEFT JOIN {comments} com ON com.commentarea=:commentarea and com.itemid = dr.id AND com.userid = :userid1 - LEFT JOIN {rating} r ON r.contextid = c.id AND r.itemid = dr.id AND r.component = :moddata - AND r.ratingarea = :ratingarea AND r.userid = :userid2 - WHERE dr.userid = :userid OR com.id IS NOT NULL OR r.id IS NOT NULL"; + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {data} d ON d.id = cm.instance + JOIN {data_records} dr ON dr.dataid = d.id + WHERE dr.userid = :userid"; $params = [ - 'modname' => 'data', 'contextlevel' => CONTEXT_MODULE, + 'modname' => 'data', 'userid' => $userid, - 'userid1' => $userid, - 'userid2' => $userid, - 'commentarea' => 'database_entry', - 'moddata' => 'mod_data', - 'ratingarea' => 'entry', ]; - $contextlist = new contextlist(); + $contextlist->add_from_sql($sql, $params); + + // Fetch contexts where the user commented. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {data} d ON d.id = cm.instance + JOIN {data_records} dr ON dr.dataid = d.id + JOIN {comments} com ON com.commentarea = :commentarea and com.itemid = dr.id + WHERE com.userid = :userid"; + + $params = [ + 'contextlevel' => CONTEXT_MODULE, + 'modname' => 'data', + 'commentarea' => 'database_entry', + 'userid' => $userid, + ]; + $contextlist->add_from_sql($sql, $params); + + // Fetch all data records. + $ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_data', 'entry', 'dr.id', $userid, true); + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {data} d ON d.id = cm.instance + JOIN {data_records} dr ON dr.dataid = d.id + {$ratingquery->join} + WHERE {$ratingquery->userwhere}"; + + $params = [ + 'contextlevel' => CONTEXT_MODULE, + 'modname' => 'data', + ] + $ratingquery->params; $contextlist->add_from_sql($sql, $params); return $contextlist; diff --git a/mod/forum/classes/privacy/provider.php b/mod/forum/classes/privacy/provider.php index 484360c805a..e75eb7498ca 100644 --- a/mod/forum/classes/privacy/provider.php +++ b/mod/forum/classes/privacy/provider.php @@ -152,46 +152,105 @@ class provider implements * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin. */ public static function get_contexts_for_userid(int $userid) : \core_privacy\local\request\contextlist { - $ratingsql = \core_rating\privacy\provider::get_sql_join('rat', 'mod_forum', 'post', 'p.id', $userid); - // Fetch all forum discussions, and forum posts. + $contextlist = new \core_privacy\local\request\contextlist(); + + $params = [ + 'modname' => 'forum', + 'contextlevel' => CONTEXT_MODULE, + 'userid' => $userid, + ]; + + // Discussion creators. $sql = "SELECT c.id FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel JOIN {modules} m ON m.id = cm.module AND m.name = :modname JOIN {forum} f ON f.id = cm.instance - LEFT JOIN {forum_discussions} d ON d.forum = f.id - LEFT JOIN {forum_posts} p ON p.discussion = d.id - LEFT JOIN {forum_digests} dig ON dig.forum = f.id AND dig.userid = :digestuserid - LEFT JOIN {forum_subscriptions} sub ON sub.forum = f.id AND sub.userid = :subuserid - LEFT JOIN {forum_track_prefs} pref ON pref.forumid = f.id AND pref.userid = :prefuserid - LEFT JOIN {forum_read} hasread ON hasread.forumid = f.id AND hasread.userid = :hasreaduserid - LEFT JOIN {forum_discussion_subs} dsub ON dsub.forum = f.id AND dsub.userid = :dsubuserid - {$ratingsql->join} - WHERE ( - p.userid = :postuserid OR - d.userid = :discussionuserid OR - dig.id IS NOT NULL OR - sub.id IS NOT NULL OR - pref.id IS NOT NULL OR - hasread.id IS NOT NULL OR - dsub.id IS NOT NULL OR - {$ratingsql->userwhere} - ) + JOIN {forum_discussions} d ON d.forum = f.id + WHERE d.userid = :userid "; - $params = [ - 'modname' => 'forum', - 'contextlevel' => CONTEXT_MODULE, - 'postuserid' => $userid, - 'discussionuserid' => $userid, - 'digestuserid' => $userid, - 'subuserid' => $userid, - 'prefuserid' => $userid, - 'hasreaduserid' => $userid, - 'dsubuserid' => $userid, - ]; - $params += $ratingsql->params; + $contextlist->add_from_sql($sql, $params); - $contextlist = new \core_privacy\local\request\contextlist(); + // Post authors. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_discussions} d ON d.forum = f.id + JOIN {forum_posts} p ON p.discussion = d.id + WHERE p.userid = :userid + "; + $contextlist->add_from_sql($sql, $params); + + // Forum digest records. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_digests} dig ON dig.forum = f.id + WHERE dig.userid = :userid + "; + $contextlist->add_from_sql($sql, $params); + + // Forum subscriptions. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_subscriptions} sub ON sub.forum = f.id + WHERE sub.userid = :userid + "; + $contextlist->add_from_sql($sql, $params); + + // Discussion subscriptions. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_discussion_subs} dsub ON dsub.forum = f.id + WHERE dsub.userid = :userid + "; + $contextlist->add_from_sql($sql, $params); + + // Discussion tracking preferences. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_track_prefs} pref ON pref.forumid = f.id + WHERE pref.userid = :userid + "; + $contextlist->add_from_sql($sql, $params); + + // Discussion read records. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_read} hasread ON hasread.forumid = f.id + WHERE hasread.userid = :userid + "; + $contextlist->add_from_sql($sql, $params); + + // Rating authors. + $ratingsql = \core_rating\privacy\provider::get_sql_join('rat', 'mod_forum', 'post', 'p.id', $userid, true); + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_discussions} d ON d.forum = f.id + JOIN {forum_posts} p ON p.discussion = d.id + {$ratingsql->join} + WHERE {$ratingsql->userwhere} + "; + $params += $ratingsql->params; $contextlist->add_from_sql($sql, $params); return $contextlist; @@ -365,30 +424,68 @@ class provider implements $userid = $user->id; list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED); + $params = $contextparams; + // Digested forums. + $sql = "SELECT + c.id AS contextid, + dig.maildigest AS maildigest + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_digests} dig ON dig.forum = f.id + WHERE ( + dig.userid = :userid AND + c.id {$contextsql} + ) + "; + $params['userid'] = $userid; + $digests = $DB->get_records_sql_menu($sql, $params); + + // Forum subscriptions. + $sql = "SELECT + c.id AS contextid, + sub.userid AS subscribed + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid + JOIN {forum} f ON f.id = cm.instance + JOIN {forum_subscriptions} sub ON sub.forum = f.id + WHERE ( + sub.userid = :userid AND + c.id {$contextsql} + ) + "; + $params['userid'] = $userid; + $subscriptions = $DB->get_records_sql_menu($sql, $params); + + // Tracked forums. $sql = "SELECT c.id AS contextid, - f.*, - cm.id AS cmid, - dig.maildigest, - sub.userid AS subscribed, pref.userid AS tracked FROM {context} c JOIN {course_modules} cm ON cm.id = c.instanceid JOIN {forum} f ON f.id = cm.instance - LEFT JOIN {forum_digests} dig ON dig.forum = f.id AND dig.userid = :digestuserid - LEFT JOIN {forum_subscriptions} sub ON sub.forum = f.id AND sub.userid = :subuserid - LEFT JOIN {forum_track_prefs} pref ON pref.forumid = f.id AND pref.userid = :prefuserid + JOIN {forum_track_prefs} pref ON pref.forumid = f.id + WHERE ( + pref.userid = :userid AND + c.id {$contextsql} + ) + "; + $params['userid'] = $userid; + $tracked = $DB->get_records_sql_menu($sql, $params); + + $sql = "SELECT + c.id AS contextid, + f.*, + cm.id AS cmid + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid + JOIN {forum} f ON f.id = cm.instance WHERE ( c.id {$contextsql} ) "; - $params = [ - 'digestuserid' => $userid, - 'subuserid' => $userid, - 'prefuserid' => $userid, - ]; $params += $contextparams; // Keep a mapping of forumid to contextid. @@ -407,9 +504,15 @@ class provider implements request_helper::export_context_files($context, $user); // Store relevant metadata about this forum instance. - static::export_digest_data($userid, $forum); - static::export_subscription_data($userid, $forum); - static::export_tracking_data($userid, $forum); + if (isset($digests[$forum->contextid])) { + static::export_digest_data($userid, $forum, $digests[$forum->contextid]); + } + if (isset($subscriptions[$forum->contextid])) { + static::export_subscription_data($userid, $forum, $subscriptions[$forum->contextid]); + } + if (isset($tracked[$forum->contextid])) { + static::export_tracking_data($userid, $forum, $tracked[$forum->contextid]); + } } $forums->close(); @@ -704,16 +807,17 @@ class provider implements * * @param int $userid The userid of the user whose data is to be exported. * @param \stdClass $forum The forum whose data is being exported. + * @param int $maildigest The mail digest setting for this forum. * @return bool Whether any data was stored. */ - protected static function export_digest_data(int $userid, \stdClass $forum) { - if (null !== $forum->maildigest) { + protected static function export_digest_data(int $userid, \stdClass $forum, int $maildigest) { + if (null !== $maildigest) { // The user has a specific maildigest preference for this forum. $a = (object) [ 'forum' => format_string($forum->name, true), ]; - switch ($forum->maildigest) { + switch ($maildigest) { case 0: $a->type = get_string('emaildigestoffshort', 'mod_forum'); break; @@ -726,7 +830,7 @@ class provider implements } writer::with_context(\context_module::instance($forum->cmid)) - ->export_metadata([], 'digestpreference', $forum->maildigest, + ->export_metadata([], 'digestpreference', $maildigest, get_string('privacy:digesttypepreference', 'mod_forum', $a)); return true; @@ -740,10 +844,11 @@ class provider implements * * @param int $userid The userid of the user whose data is to be exported. * @param \stdClass $forum The forum whose data is being exported. + * @param int $subscribed if the user is subscribed * @return bool Whether any data was stored. */ - protected static function export_subscription_data(int $userid, \stdClass $forum) { - if (null !== $forum->subscribed) { + protected static function export_subscription_data(int $userid, \stdClass $forum, int $subscribed) { + if (null !== $subscribed) { // The user is subscribed to this forum. writer::with_context(\context_module::instance($forum->cmid)) ->export_metadata([], 'subscriptionpreference', 1, get_string('privacy:subscribedtoforum', 'mod_forum')); @@ -798,10 +903,11 @@ class provider implements * * @param int $userid The userid of the user whose data is to be exported. * @param \stdClass $forum The forum whose data is being exported. + * @param int $tracke if the user is subscribed * @return bool Whether any data was stored. */ - protected static function export_tracking_data(int $userid, \stdClass $forum) { - if (null !== $forum->tracked) { + protected static function export_tracking_data(int $userid, \stdClass $forum, int $tracked) { + if (null !== $tracked) { // The user has a main preference to track all forums, but has opted out of this one. writer::with_context(\context_module::instance($forum->cmid)) ->export_metadata([], 'trackreadpreference', 0, get_string('privacy:readtrackingdisabled', 'mod_forum')); diff --git a/mod/glossary/classes/privacy/provider.php b/mod/glossary/classes/privacy/provider.php index 66cf6966cf5..5f52b06f1e6 100644 --- a/mod/glossary/classes/privacy/provider.php +++ b/mod/glossary/classes/privacy/provider.php @@ -80,26 +80,55 @@ class provider implements * @return contextlist the list of contexts containing user info for the user. */ public static function get_contexts_for_userid(int $userid) : contextlist { - $ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_glossary', 'entry', 'ge.id', $userid); + $contextlist = new contextlist(); + // Glossary entries. $sql = "SELECT c.id FROM {context} c - INNER JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel - INNER JOIN {modules} m ON m.id = cm.module AND m.name = :modname - INNER JOIN {glossary} g ON g.id = cm.instance - INNER JOIN {glossary_entries} ge ON ge.glossaryid = g.id - LEFT JOIN {comments} com ON com.commentarea =:commentarea AND com.itemid = ge.id - {$ratingquery->join} - WHERE ge.userid = :glossaryentryuserid OR com.userid = :commentuserid OR {$ratingquery->userwhere}"; + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {glossary} g ON g.id = cm.instance + JOIN {glossary_entries} ge ON ge.glossaryid = g.id + WHERE ge.userid = :glossaryentryuserid"; $params = [ 'contextlevel' => CONTEXT_MODULE, 'modname' => 'glossary', 'commentarea' => 'glossary_entry', 'glossaryentryuserid' => $userid, - 'commentuserid' => $userid, - ] + $ratingquery->params; + ]; + $contextlist->add_from_sql($sql, $params); - $contextlist = new contextlist(); + // Where the user has rated something. + $ratingquery = \core_rating\privacy\provider::get_sql_join('r', 'mod_glossary', 'entry', 'ge.id', $userid, true); + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {glossary} g ON g.id = cm.instance + JOIN {glossary_entries} ge ON ge.glossaryid = g.id + {$ratingquery->join} + WHERE {$ratingquery->userwhere}"; + $params = [ + 'contextlevel' => CONTEXT_MODULE, + 'modname' => 'glossary', + ] + $ratingquery->params; + $contextlist->add_from_sql($sql, $params); + + // Comments. + $sql = "SELECT c.id + FROM {context} c + JOIN {course_modules} cm ON cm.id = c.instanceid AND c.contextlevel = :contextlevel + JOIN {modules} m ON m.id = cm.module AND m.name = :modname + JOIN {glossary} g ON g.id = cm.instance + JOIN {glossary_entries} ge ON ge.glossaryid = g.id + JOIN {comments} com ON com.commentarea =:commentarea AND com.itemid = ge.id + WHERE com.userid = :commentuserid"; + $params = [ + 'contextlevel' => CONTEXT_MODULE, + 'modname' => 'glossary', + 'commentarea' => 'glossary_entry', + 'commentuserid' => $userid, + ]; $contextlist->add_from_sql($sql, $params); return $contextlist; diff --git a/rating/classes/privacy/provider.php b/rating/classes/privacy/provider.php index e1d87afbfca..864ec4f4872 100644 --- a/rating/classes/privacy/provider.php +++ b/rating/classes/privacy/provider.php @@ -129,26 +129,41 @@ class provider implements /** * Get the SQL required to find all submission items where this user has had any involvements. * + * If possible an inner join should be used. + * * @param string $alias The name of the table alias to use. * @param string $component The na eof the component to fetch ratings for. * @param string $ratingarea The rating area to fetch results for. * @param string $itemidjoin The right-hand-side of the JOIN ON clause. * @param int $userid The ID of the user being stored. + * @param bool $innerjoin Whether to use an inner join (preferred) * @return \stdClass */ - public static function get_sql_join($alias, $component, $ratingarea, $itemidjoin, $userid) { + public static function get_sql_join($alias, $component, $ratingarea, $itemidjoin, $userid, $innerjoin = false) { static $count = 0; $count++; - // Join the rating table with the specified alias and the relevant join params. - $join = "LEFT JOIN {rating} {$alias} ON "; - $join .= "{$alias}.userid = :ratinguserid{$count} AND "; - $join .= "{$alias}.component = :ratingcomponent{$count} AND "; - $join .= "{$alias}.ratingarea = :ratingarea{$count} AND "; - $join .= "{$alias}.itemid = {$itemidjoin}"; + $userwhere = ''; - // Match against the specified user. - $userwhere = "{$alias}.id IS NOT NULL"; + if ($innerjoin) { + // Join the rating table with the specified alias and the relevant join params. + $join = "JOIN {rating} {$alias} ON "; + $join .= "{$alias}.itemid = {$itemidjoin}"; + + $userwhere .= "{$alias}.userid = :ratinguserid{$count} AND "; + $userwhere .= "{$alias}.component = :ratingcomponent{$count} AND "; + $userwhere .= "{$alias}.ratingarea = :ratingarea{$count}"; + } else { + // Join the rating table with the specified alias and the relevant join params. + $join = "LEFT JOIN {rating} {$alias} ON "; + $join .= "{$alias}.userid = :ratinguserid{$count} AND "; + $join .= "{$alias}.component = :ratingcomponent{$count} AND "; + $join .= "{$alias}.ratingarea = :ratingarea{$count} AND "; + $join .= "{$alias}.itemid = {$itemidjoin}"; + + // Match against the specified user. + $userwhere = "{$alias}.id IS NOT NULL"; + } $params = [ 'ratingcomponent' . $count => $component, diff --git a/rating/tests/privacy_provider_test.php b/rating/tests/privacy_provider_test.php index 7f0fb179331..5f92c0f857a 100644 --- a/rating/tests/privacy_provider_test.php +++ b/rating/tests/privacy_provider_test.php @@ -153,6 +153,94 @@ class core_rating_privacy_testcase extends \core_privacy\tests\provider_testcase $this->assertFalse(isset($courses[$course3->id])); } + /** + * Ensure that the get_sql_join function returns valid SQL which returns the correct list of rated itemids. + * This makes use of the optional inner join argument. + */ + public function test_get_sql_join_inner() { + global $DB; + $this->resetAfterTest(); + + $course1 = $this->getDataGenerator()->create_course(); + $course2 = $this->getDataGenerator()->create_course(); + $course3 = $this->getDataGenerator()->create_course(); + + $u1 = $this->getDataGenerator()->create_user(); + $u2 = $this->getDataGenerator()->create_user(); + $u3 = $this->getDataGenerator()->create_user(); + + // Rate the courses. + $rm = new rating_manager(); + $ratingoptions = (object) [ + 'component' => 'core_course', + 'ratingarea' => 'course', + 'scaleid' => 100, + ]; + + // Rate all courses as u1, and something else in the same context. + $this->rate_as_user($u1->id, 'core_course', 'course', $course1->id, \context_course::instance($course1->id), 25); + $this->rate_as_user($u1->id, 'core_course', 'course', $course2->id, \context_course::instance($course2->id), 50); + $this->rate_as_user($u1->id, 'core_course', 'course', $course3->id, \context_course::instance($course3->id), 75); + $this->rate_as_user($u1->id, 'core_course', 'files', $course3->id, \context_course::instance($course3->id), 99); + + // Rate course2 as u2, and something else in a different context/component.. + $this->rate_as_user($u2->id, 'core_course', 'course', $course2->id, \context_course::instance($course2->id), 90); + $this->rate_as_user($u2->id, 'user', 'user', $u3->id, \context_user::instance($u3->id), 10); + + // Return any course which the u1 has rated. + // u1 rated all three courses. + $ratingquery = provider::get_sql_join('r', 'core_course', 'course', 'c.id', $u1->id, true); + $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}"; + $courses = $DB->get_records_sql($sql, $ratingquery->params); + + $this->assertCount(3, $courses); + $this->assertTrue(isset($courses[$course1->id])); + $this->assertTrue(isset($courses[$course2->id])); + $this->assertTrue(isset($courses[$course3->id])); + + // User u1 rated files in course 3 only. + $ratingquery = provider::get_sql_join('r', 'core_course', 'files', 'c.id', $u1->id, true); + $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}"; + $courses = $DB->get_records_sql($sql, $ratingquery->params); + + $this->assertCount(1, $courses); + $this->assertFalse(isset($courses[$course1->id])); + $this->assertFalse(isset($courses[$course2->id])); + $this->assertTrue(isset($courses[$course3->id])); + + // Return any course which the u2 has rated. + // User u2 rated only course 2. + $ratingquery = provider::get_sql_join('r', 'core_course', 'course', 'c.id', $u2->id, true); + $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}"; + $courses = $DB->get_records_sql($sql, $ratingquery->params); + + $this->assertCount(1, $courses); + $this->assertFalse(isset($courses[$course1->id])); + $this->assertTrue(isset($courses[$course2->id])); + $this->assertFalse(isset($courses[$course3->id])); + + // User u2 rated u3. + $ratingquery = provider::get_sql_join('r', 'user', 'user', 'u.id', $u2->id, true); + $sql = "SELECT u.id FROM {user} u {$ratingquery->join} WHERE {$ratingquery->userwhere}"; + $users = $DB->get_records_sql($sql, $ratingquery->params); + + $this->assertCount(1, $users); + $this->assertFalse(isset($users[$u1->id])); + $this->assertFalse(isset($users[$u2->id])); + $this->assertTrue(isset($users[$u3->id])); + + // Return any course which the u3 has rated. + // User u3 did not rate anything. + $ratingquery = provider::get_sql_join('r', 'core_course', 'course', 'c.id', $u3->id, true); + $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}"; + $courses = $DB->get_records_sql($sql, $ratingquery->params); + + $this->assertCount(0, $courses); + $this->assertFalse(isset($courses[$course1->id])); + $this->assertFalse(isset($courses[$course2->id])); + $this->assertFalse(isset($courses[$course3->id])); + } + /** * Ensure that export_area_ratings exports all ratings that a user has made, and all ratings for a users own content. */