See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 401]
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 * Condition main class. 19 * 20 * @package availability_grouping 21 * @copyright 2014 The Open University 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace availability_grouping; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 /** 30 * Condition main class. 31 * 32 * @package availability_grouping 33 * @copyright 2014 The Open University 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 class condition extends \core_availability\condition { 37 /** @var array Array from grouping id => name */ 38 protected static $groupingnames = array(); 39 40 /** @var int ID of grouping that this condition requires */ 41 protected $groupingid = 0; 42 43 /** @var bool If true, indicates that activity $cm->grouping is used */ 44 protected $activitygrouping = false; 45 46 /** 47 * Constructor. 48 * 49 * @param \stdClass $structure Data structure from JSON decode 50 * @throws \coding_exception If invalid data structure. 51 */ 52 public function __construct($structure) { 53 // Get grouping id. 54 if (isset($structure->id)) { 55 if (is_int($structure->id)) { 56 $this->groupingid = $structure->id; 57 } else { 58 throw new \coding_exception('Invalid ->id for grouping condition'); 59 } 60 } else if (isset($structure->activity)) { 61 if (is_bool($structure->activity) && $structure->activity) { 62 $this->activitygrouping = true; 63 } else { 64 throw new \coding_exception('Invalid ->activity for grouping condition'); 65 } 66 } else { 67 throw new \coding_exception('Missing ->id / ->activity for grouping condition'); 68 } 69 } 70 71 public function save() { 72 $result = (object)array('type' => 'grouping'); 73 if ($this->groupingid) { 74 $result->id = $this->groupingid; 75 } else { 76 $result->activity = true; 77 } 78 return $result; 79 } 80 81 public function is_available($not, \core_availability\info $info, $grabthelot, $userid) { 82 $context = \context_course::instance($info->get_course()->id); 83 $allow = true; 84 if (!has_capability('moodle/site:accessallgroups', $context, $userid)) { 85 // If the activity has 'group members only' and you don't have accessallgroups... 86 $groups = $info->get_modinfo()->get_groups($this->get_grouping_id($info)); 87 if (!$groups) { 88 // ...and you don't belong to a group, then set it so you can't see/access it. 89 $allow = false; 90 } 91 92 // The NOT condition applies before accessallgroups (i.e. if you 93 // set something to be available to those NOT in grouping X, 94 // people with accessallgroups can still access it even if 95 // they are in grouping X). 96 if ($not) { 97 $allow = !$allow; 98 } 99 } 100 return $allow; 101 } 102 103 /** 104 * Gets the actual grouping id for the condition. This is either a specified 105 * id, or a special flag indicating that we use the one for the current cm. 106 * 107 * @param \core_availability\info $info Info about context cm 108 * @return int Grouping id 109 * @throws \coding_exception If it's set to use a cm but there isn't grouping 110 */ 111 protected function get_grouping_id(\core_availability\info $info) { 112 if ($this->activitygrouping) { 113 $groupingid = $info->get_course_module()->groupingid; 114 if (!$groupingid) { 115 throw new \coding_exception( 116 'Not supposed to be able to turn on activitygrouping when no grouping'); 117 } 118 return $groupingid; 119 } else { 120 return $this->groupingid; 121 } 122 } 123 124 public function get_description($full, $not, \core_availability\info $info) { 125 global $DB; 126 $course = $info->get_course(); 127 128 // Need to get the name for the grouping. Unfortunately this requires 129 // a database query. To save queries, get all groupings for course at 130 // once in a static cache. 131 $groupingid = $this->get_grouping_id($info); 132 if (!array_key_exists($groupingid, self::$groupingnames)) { 133 $coursegroupings = $DB->get_records( 134 'groupings', array('courseid' => $course->id), '', 'id, name'); 135 foreach ($coursegroupings as $rec) { 136 self::$groupingnames[$rec->id] = $rec->name; 137 } 138 } 139 140 // If it still doesn't exist, it must have been misplaced. 141 if (!array_key_exists($groupingid, self::$groupingnames)) { 142 $name = get_string('missing', 'availability_grouping'); 143 } else { 144 // Not safe to call format_string here; use the special function to call it later. 145 $name = self::description_format_string(self::$groupingnames[$groupingid]); 146 } 147 148 return get_string($not ? 'requires_notgrouping' : 'requires_grouping', 149 'availability_grouping', $name); 150 } 151 152 protected function get_debug_string() { 153 if ($this->activitygrouping) { 154 return 'CM'; 155 } else { 156 return '#' . $this->groupingid; 157 } 158 } 159 160 /** 161 * Include this condition only if we are including groups in restore, or 162 * if it's a generic 'same activity' one. 163 * 164 * @param int $restoreid The restore Id. 165 * @param int $courseid The ID of the course. 166 * @param base_logger $logger The logger being used. 167 * @param string $name Name of item being restored. 168 * @param base_task $task The task being performed. 169 * 170 * @return Integer groupid 171 */ 172 public function include_after_restore($restoreid, $courseid, \base_logger $logger, 173 $name, \base_task $task) { 174 return !$this->groupingid || $task->get_setting_value('groups'); 175 } 176 177 public function update_after_restore($restoreid, $courseid, \base_logger $logger, $name) { 178 global $DB; 179 if (!$this->groupingid) { 180 // If using 'same as activity' option, no need to change it. 181 return false; 182 } 183 $rec = \restore_dbops::get_backup_ids_record($restoreid, 'grouping', $this->groupingid); 184 if (!$rec || !$rec->newitemid) { 185 // If we are on the same course (e.g. duplicate) then we can just 186 // use the existing one. 187 if ($DB->record_exists('groupings', 188 array('id' => $this->groupingid, 'courseid' => $courseid))) { 189 return false; 190 } 191 // Otherwise it's a warning. 192 $this->groupingid = -1; 193 $logger->process('Restored item (' . $name . 194 ') has availability condition on grouping that was not restored', 195 \backup::LOG_WARNING); 196 } else { 197 $this->groupingid = (int)$rec->newitemid; 198 } 199 return true; 200 } 201 202 public function update_dependency_id($table, $oldid, $newid) { 203 if ($table === 'groupings' && (int)$this->groupingid === (int)$oldid) { 204 $this->groupingid = $newid; 205 return true; 206 } else { 207 return false; 208 } 209 } 210 211 /** 212 * Wipes the static cache used to store grouping names. 213 */ 214 public static function wipe_static_cache() { 215 self::$groupingnames = array(); 216 } 217 218 public function is_applied_to_user_lists() { 219 // Grouping conditions are assumed to be 'permanent', so they affect the 220 // display of user lists for activities. 221 return true; 222 } 223 224 public function filter_user_list(array $users, $not, \core_availability\info $info, 225 \core_availability\capability_checker $checker) { 226 global $CFG, $DB; 227 228 // If the array is empty already, just return it. 229 if (!$users) { 230 return $users; 231 } 232 233 // List users for this course who match the condition. 234 $groupingusers = $DB->get_records_sql(" 235 SELECT DISTINCT gm.userid 236 FROM {groupings_groups} gg 237 JOIN {groups_members} gm ON gm.groupid = gg.groupid 238 WHERE gg.groupingid = ?", 239 array($this->get_grouping_id($info))); 240 241 // List users who have access all groups. 242 $aagusers = $checker->get_users_by_capability('moodle/site:accessallgroups'); 243 244 // Filter the user list. 245 $result = array(); 246 foreach ($users as $id => $user) { 247 // Always include users with access all groups. 248 if (array_key_exists($id, $aagusers)) { 249 $result[$id] = $user; 250 continue; 251 } 252 // Other users are included or not based on grouping membership. 253 $allow = array_key_exists($id, $groupingusers); 254 if ($not) { 255 $allow = !$allow; 256 } 257 if ($allow) { 258 $result[$id] = $user; 259 } 260 } 261 return $result; 262 } 263 264 /** 265 * Returns a JSON object which corresponds to a condition of this type. 266 * 267 * Intended for unit testing, as normally the JSON values are constructed 268 * by JavaScript code. 269 * 270 * @param int $groupingid Required grouping id (0 = grouping linked to activity) 271 * @return stdClass Object representing condition 272 */ 273 public static function get_json($groupingid = 0) { 274 $result = (object)array('type' => 'grouping'); 275 if ($groupingid) { 276 $result->id = (int)$groupingid; 277 } else { 278 $result->activity = true; 279 } 280 return $result; 281 } 282 283 public function get_user_list_sql($not, \core_availability\info $info, $onlyactive) { 284 global $DB; 285 286 // Get enrolled users with access all groups. These always are allowed. 287 list($aagsql, $aagparams) = get_enrolled_sql( 288 $info->get_context(), 'moodle/site:accessallgroups', 0, $onlyactive); 289 290 // Get all enrolled users. 291 list ($enrolsql, $enrolparams) = 292 get_enrolled_sql($info->get_context(), '', 0, $onlyactive); 293 294 // Condition for specified or any group. 295 $matchparams = array(); 296 $matchsql = "SELECT 1 297 FROM {groups_members} gm 298 JOIN {groupings_groups} gg ON gg.groupid = gm.groupid 299 WHERE gm.userid = userids.id 300 AND gg.groupingid = " . 301 self::unique_sql_parameter($matchparams, $this->get_grouping_id($info)); 302 303 // Overall query combines all this. 304 $condition = $not ? 'NOT' : ''; 305 $sql = "SELECT userids.id 306 FROM ($enrolsql) userids 307 WHERE (userids.id IN ($aagsql)) OR $condition EXISTS ($matchsql)"; 308 return array($sql, array_merge($enrolparams, $aagparams, $matchparams)); 309 } 310 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body