1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Privacy Subsystem implementation for core_ratings. 19 * 20 * @package core_rating 21 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_rating\privacy; 26 27 use \core_privacy\local\metadata\collection; 28 use \core_privacy\local\request\userlist; 29 30 defined('MOODLE_INTERNAL') || die(); 31 32 require_once($CFG->dirroot . '/rating/lib.php'); 33 34 /** 35 * Privacy Subsystem implementation for core_ratings. 36 * 37 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class provider implements 41 // The ratings subsystem contains data. 42 \core_privacy\local\metadata\provider, 43 44 // The ratings subsystem is only ever used to store data for other components. 45 // It does not store any data of its own and does not need to implement the \core_privacy\local\request\subsystem\provider 46 // as a result. 47 48 // The ratings subsystem provides a data service to other components. 49 \core_privacy\local\request\subsystem\plugin_provider, 50 \core_privacy\local\request\shared_userlist_provider 51 { 52 53 /** 54 * Returns metadata about the ratings subsystem. 55 * 56 * @param collection $collection The initialised collection to add items to. 57 * @return collection A listing of user data stored through the subsystem. 58 */ 59 public static function get_metadata(collection $collection) : collection { 60 // The table 'rating' cotains data that a user has entered. 61 // It stores the user-entered rating alongside a mapping to describe what was mapped. 62 $collection->add_database_table('rating', [ 63 'rating' => 'privacy:metadata:rating:rating', 64 'userid' => 'privacy:metadata:rating:userid', 65 'timecreated' => 'privacy:metadata:rating:timecreated', 66 'timemodified' => 'privacy:metadata:rating:timemodified', 67 ], 'privacy:metadata:rating'); 68 69 return $collection; 70 } 71 72 /** 73 * Export all ratings which match the specified component, areaid, and itemid. 74 * 75 * If requesting ratings for a users own content, and you wish to include all ratings of that content, specify 76 * $onlyuser as false. 77 * 78 * When requesting ratings for another users content, you should only export the ratings that the specified user 79 * made themselves. 80 * 81 * @param int $userid The user whose information is to be exported 82 * @param \context $context The context being stored. 83 * @param array $subcontext The subcontext within the context to export this information 84 * @param string $component The component to fetch data from 85 * @param string $ratingarea The ratingarea that the data was stored in within the component 86 * @param int $itemid The itemid within that ratingarea 87 * @param bool $onlyuser Whether to only export ratings that the current user has made, or all ratings 88 */ 89 public static function export_area_ratings( 90 int $userid, 91 \context $context, 92 array $subcontext, 93 string $component, 94 string $ratingarea, 95 int $itemid, 96 bool $onlyuser = true 97 ) { 98 global $DB; 99 100 $rm = new \rating_manager(); 101 $ratings = $rm->get_all_ratings_for_item((object) [ 102 'context' => $context, 103 'component' => $component, 104 'ratingarea' => $ratingarea, 105 'itemid' => $itemid, 106 ]); 107 108 if ($onlyuser) { 109 $ratings = array_filter($ratings, function($rating) use ($userid){ 110 return ($rating->userid == $userid); 111 }); 112 } 113 114 if (empty($ratings)) { 115 return; 116 } 117 118 $toexport = array_map(function($rating) { 119 return (object) [ 120 'rating' => $rating->rating, 121 'author' => $rating->userid, 122 ]; 123 }, $ratings); 124 125 $writer = \core_privacy\local\request\writer::with_context($context) 126 ->export_related_data($subcontext, 'rating', $toexport); 127 } 128 129 /** 130 * Get the SQL required to find all submission items where this user has had any involvements. 131 * 132 * If possible an inner join should be used. 133 * 134 * @param string $alias The name of the table alias to use. 135 * @param string $component The na eof the component to fetch ratings for. 136 * @param string $ratingarea The rating area to fetch results for. 137 * @param string $itemidjoin The right-hand-side of the JOIN ON clause. 138 * @param int $userid The ID of the user being stored. 139 * @param bool $innerjoin Whether to use an inner join (preferred) 140 * @return \stdClass 141 */ 142 public static function get_sql_join($alias, $component, $ratingarea, $itemidjoin, $userid, $innerjoin = false) { 143 static $count = 0; 144 $count++; 145 146 $userwhere = ''; 147 148 if ($innerjoin) { 149 // Join the rating table with the specified alias and the relevant join params. 150 $join = "JOIN {rating} {$alias} ON "; 151 $join .= "{$alias}.itemid = {$itemidjoin}"; 152 153 $userwhere .= "{$alias}.userid = :ratinguserid{$count} AND "; 154 $userwhere .= "{$alias}.component = :ratingcomponent{$count} AND "; 155 $userwhere .= "{$alias}.ratingarea = :ratingarea{$count}"; 156 } else { 157 // Join the rating table with the specified alias and the relevant join params. 158 $join = "LEFT JOIN {rating} {$alias} ON "; 159 $join .= "{$alias}.userid = :ratinguserid{$count} AND "; 160 $join .= "{$alias}.component = :ratingcomponent{$count} AND "; 161 $join .= "{$alias}.ratingarea = :ratingarea{$count} AND "; 162 $join .= "{$alias}.itemid = {$itemidjoin}"; 163 164 // Match against the specified user. 165 $userwhere = "{$alias}.id IS NOT NULL"; 166 } 167 168 $params = [ 169 'ratingcomponent' . $count => $component, 170 'ratingarea' . $count => $ratingarea, 171 'ratinguserid' . $count => $userid, 172 ]; 173 174 $return = (object) [ 175 'join' => $join, 176 'params' => $params, 177 'userwhere' => $userwhere, 178 ]; 179 return $return; 180 } 181 182 /** 183 * Deletes all ratings for a specified context, component, ratingarea and itemid. 184 * 185 * Only delete ratings when the item itself was deleted. 186 * 187 * We never delete ratings for one user but not others - this may affect grades, therefore ratings 188 * made by particular user are not considered personal information. 189 * 190 * @param \context $context Details about which context to delete ratings for. 191 * @param string $component Component to delete. 192 * @param string $ratingarea Rating area to delete. 193 * @param int $itemid The item ID for use with deletion. 194 */ 195 public static function delete_ratings(\context $context, string $component = null, 196 string $ratingarea = null, int $itemid = null) { 197 global $DB; 198 199 $options = ['contextid' => $context->id]; 200 if ($component) { 201 $options['component'] = $component; 202 } 203 if ($ratingarea) { 204 $options['ratingarea'] = $ratingarea; 205 } 206 if ($itemid) { 207 $options['itemid'] = $itemid; 208 } 209 210 $DB->delete_records('rating', $options); 211 } 212 213 /** 214 * Deletes all tag instances for given context, component, itemtype using subquery for itemids 215 * 216 * In most situations you will want to specify $userid as null. Per-user tag instances 217 * are possible in Tags API, however there are no components or standard plugins that actually use them. 218 * 219 * @param \context $context Details about which context to delete ratings for. 220 * @param string $component Component to delete. 221 * @param string $ratingarea Rating area to delete. 222 * @param string $itemidstest an SQL fragment that the itemid must match. Used 223 * in the query like WHERE itemid $itemidstest. Must use named parameters, 224 * and may not use named parameters called contextid, component or ratingarea. 225 * @param array $params any query params used by $itemidstest. 226 */ 227 public static function delete_ratings_select(\context $context, string $component, 228 string $ratingarea, $itemidstest, $params = []) { 229 global $DB; 230 $params += ['contextid' => $context->id, 'component' => $component, 'ratingarea' => $ratingarea]; 231 $DB->delete_records_select('rating', 232 'contextid = :contextid AND component = :component AND ratingarea = :ratingarea AND itemid ' . $itemidstest, 233 $params); 234 } 235 236 /** 237 * Add the list of users who have rated in the specified constraints. 238 * 239 * @param userlist $userlist The userlist to add the users to. 240 * @param string $alias An alias prefix to use for rating selects to avoid interference with your own sql. 241 * @param string $component The component to check. 242 * @param string $area The rating area to check. 243 * @param string $insql The SQL to use in a sub-select for the itemid query. 244 * @param array $params The params required for the insql. 245 */ 246 public static function get_users_in_context_from_sql( 247 userlist $userlist, string $alias, string $component, string $area, string $insql, $params) { 248 // Discussion authors. 249 $sql = "SELECT {$alias}.userid 250 FROM {rating} {$alias} 251 WHERE {$alias}.component = :{$alias}component 252 AND {$alias}.ratingarea = :{$alias}ratingarea 253 AND {$alias}.itemid IN ({$insql})"; 254 255 $params["{$alias}component"] = $component; 256 $params["{$alias}ratingarea"] = $area; 257 258 $userlist->add_from_sql('userid', $sql, $params); 259 } 260 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body