| 1 | efrain | 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 |  * Base class for conditional availability information (for module or section).
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * @package core_availability
 | 
        
           |  |  | 21 |  * @copyright 2014 The Open University
 | 
        
           |  |  | 22 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 23 |  */
 | 
        
           |  |  | 24 |   | 
        
           |  |  | 25 | namespace core_availability;
 | 
        
           |  |  | 26 |   | 
        
           |  |  | 27 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 28 |   | 
        
           |  |  | 29 | /**
 | 
        
           |  |  | 30 |  * Base class for conditional availability information (for module or section).
 | 
        
           |  |  | 31 |  *
 | 
        
           |  |  | 32 |  * @package core_availability
 | 
        
           |  |  | 33 |  * @copyright 2014 The Open University
 | 
        
           |  |  | 34 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 35 |  */
 | 
        
           |  |  | 36 | abstract class info {
 | 
        
           |  |  | 37 |     /** @var \stdClass Course */
 | 
        
           |  |  | 38 |     protected $course;
 | 
        
           |  |  | 39 |   | 
        
           |  |  | 40 |     /** @var \course_modinfo Modinfo (available only during some functions) */
 | 
        
           |  |  | 41 |     protected $modinfo = null;
 | 
        
           |  |  | 42 |   | 
        
           |  |  | 43 |     /** @var bool Visibility flag (eye icon) */
 | 
        
           |  |  | 44 |     protected $visible;
 | 
        
           |  |  | 45 |   | 
        
           |  |  | 46 |     /** @var string Availability data as JSON string */
 | 
        
           |  |  | 47 |     protected $availability;
 | 
        
           |  |  | 48 |   | 
        
           |  |  | 49 |     /** @var tree Availability configuration, decoded from JSON; null if unset */
 | 
        
           |  |  | 50 |     protected $availabilitytree;
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 |     /** @var array The groups each user belongs to. */
 | 
        
           |  |  | 53 |     protected $groups = [];
 | 
        
           |  |  | 54 |   | 
        
           |  |  | 55 |     /** @var array|null Array of information about current restore if any */
 | 
        
           |  |  | 56 |     protected static $restoreinfo = null;
 | 
        
           |  |  | 57 |   | 
        
           |  |  | 58 |     /**
 | 
        
           |  |  | 59 |      * Constructs with item details.
 | 
        
           |  |  | 60 |      *
 | 
        
           |  |  | 61 |      * @param \stdClass $course Course object
 | 
        
           |  |  | 62 |      * @param int $visible Value of visible flag (eye icon)
 | 
        
           |  |  | 63 |      * @param string $availability Availability definition (JSON format) or null
 | 
        
           |  |  | 64 |      */
 | 
        
           |  |  | 65 |     public function __construct($course, $visible, $availability) {
 | 
        
           |  |  | 66 |         // Set basic values.
 | 
        
           |  |  | 67 |         $this->course = $course;
 | 
        
           |  |  | 68 |         $this->visible = (bool)$visible;
 | 
        
           |  |  | 69 |         $this->availability = $availability;
 | 
        
           |  |  | 70 |     }
 | 
        
           |  |  | 71 |   | 
        
           |  |  | 72 |     /**
 | 
        
           |  |  | 73 |      * Obtains the course associated with this availability information.
 | 
        
           |  |  | 74 |      *
 | 
        
           |  |  | 75 |      * @return \stdClass Moodle course object
 | 
        
           |  |  | 76 |      */
 | 
        
           |  |  | 77 |     public function get_course() {
 | 
        
           |  |  | 78 |         return $this->course;
 | 
        
           |  |  | 79 |     }
 | 
        
           |  |  | 80 |   | 
        
           |  |  | 81 |     /**
 | 
        
           |  |  | 82 |      * Gets context used for checking capabilities for this item.
 | 
        
           |  |  | 83 |      *
 | 
        
           |  |  | 84 |      * @return \context Context for this item
 | 
        
           |  |  | 85 |      */
 | 
        
           |  |  | 86 |     abstract public function get_context();
 | 
        
           |  |  | 87 |   | 
        
           |  |  | 88 |     /**
 | 
        
           |  |  | 89 |      * Obtains the modinfo associated with this availability information.
 | 
        
           |  |  | 90 |      *
 | 
        
           |  |  | 91 |      * Note: This field is available ONLY for use by conditions when calculating
 | 
        
           |  |  | 92 |      * availability or information.
 | 
        
           |  |  | 93 |      *
 | 
        
           |  |  | 94 |      * @return \course_modinfo Modinfo
 | 
        
           |  |  | 95 |      * @throws \coding_exception If called at incorrect times
 | 
        
           |  |  | 96 |      */
 | 
        
           |  |  | 97 |     public function get_modinfo() {
 | 
        
           |  |  | 98 |         if (!$this->modinfo) {
 | 
        
           |  |  | 99 |             throw new \coding_exception(
 | 
        
           |  |  | 100 |                     'info::get_modinfo available only during condition checking');
 | 
        
           |  |  | 101 |         }
 | 
        
           |  |  | 102 |         return $this->modinfo;
 | 
        
           |  |  | 103 |     }
 | 
        
           |  |  | 104 |   | 
        
           |  |  | 105 |     /**
 | 
        
           |  |  | 106 |      * Gets the availability tree, decoding it if not already done.
 | 
        
           |  |  | 107 |      *
 | 
        
           |  |  | 108 |      * @return tree Availability tree
 | 
        
           |  |  | 109 |      */
 | 
        
           |  |  | 110 |     public function get_availability_tree() {
 | 
        
           |  |  | 111 |         if (is_null($this->availabilitytree)) {
 | 
        
           |  |  | 112 |             if (is_null($this->availability)) {
 | 
        
           |  |  | 113 |                 throw new \coding_exception(
 | 
        
           |  |  | 114 |                         'Cannot call get_availability_tree with null availability');
 | 
        
           |  |  | 115 |             }
 | 
        
           |  |  | 116 |             $this->availabilitytree = $this->decode_availability($this->availability, true);
 | 
        
           |  |  | 117 |         }
 | 
        
           |  |  | 118 |         return $this->availabilitytree;
 | 
        
           |  |  | 119 |     }
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 |     /**
 | 
        
           |  |  | 122 |      * Decodes availability data from JSON format.
 | 
        
           |  |  | 123 |      *
 | 
        
           |  |  | 124 |      * This function also validates the retrieved data as follows:
 | 
        
           |  |  | 125 |      * 1. Data that does not meet the API-defined structure causes a
 | 
        
           |  |  | 126 |      *    coding_exception (this should be impossible unless there is
 | 
        
           |  |  | 127 |      *    a system bug or somebody manually hacks the database).
 | 
        
           |  |  | 128 |      * 2. Data that meets the structure but cannot be implemented (e.g.
 | 
        
           |  |  | 129 |      *    reference to missing plugin or to module that doesn't exist) is
 | 
        
           |  |  | 130 |      *    either silently discarded (if $lax is true) or causes a
 | 
        
           |  |  | 131 |      *    coding_exception (if $lax is false).
 | 
        
           |  |  | 132 |      *
 | 
        
           |  |  | 133 |      * @param string $availability Availability string in JSON format
 | 
        
           |  |  | 134 |      * @param boolean $lax If true, throw exceptions only for invalid structure
 | 
        
           |  |  | 135 |      * @return tree Availability tree
 | 
        
           |  |  | 136 |      * @throws \coding_exception If data is not valid JSON format
 | 
        
           |  |  | 137 |      */
 | 
        
           |  |  | 138 |     protected function decode_availability($availability, $lax) {
 | 
        
           |  |  | 139 |         // Decode JSON data.
 | 
        
           |  |  | 140 |         $structure = json_decode($availability);
 | 
        
           |  |  | 141 |         if (is_null($structure)) {
 | 
        
           |  |  | 142 |             throw new \coding_exception('Invalid availability text', $availability);
 | 
        
           |  |  | 143 |         }
 | 
        
           |  |  | 144 |   | 
        
           |  |  | 145 |         // Recursively decode tree.
 | 
        
           |  |  | 146 |         return new tree($structure, $lax);
 | 
        
           |  |  | 147 |     }
 | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 |     /**
 | 
        
           |  |  | 150 |      * Determines whether this particular item is currently available
 | 
        
           |  |  | 151 |      * according to the availability criteria.
 | 
        
           |  |  | 152 |      *
 | 
        
           |  |  | 153 |      * - This does not include the 'visible' setting (i.e. this might return
 | 
        
           |  |  | 154 |      *   true even if visible is false); visible is handled independently.
 | 
        
           |  |  | 155 |      * - This does not take account of the viewhiddenactivities capability.
 | 
        
           |  |  | 156 |      *   That should apply later.
 | 
        
           |  |  | 157 |      *
 | 
        
           |  |  | 158 |      * Depending on options selected, a description of the restrictions which
 | 
        
           |  |  | 159 |      * mean the student can't view it (in HTML format) may be stored in
 | 
        
           |  |  | 160 |      * $information. If there is nothing in $information and this function
 | 
        
           |  |  | 161 |      * returns false, then the activity should not be displayed at all.
 | 
        
           |  |  | 162 |      *
 | 
        
           |  |  | 163 |      * This function displays debugging() messages if the availability
 | 
        
           |  |  | 164 |      * information is invalid.
 | 
        
           |  |  | 165 |      *
 | 
        
           |  |  | 166 |      * @param string $information String describing restrictions in HTML format
 | 
        
           |  |  | 167 |      * @param bool $grabthelot Performance hint: if true, caches information
 | 
        
           |  |  | 168 |      *   required for all course-modules, to make the front page and similar
 | 
        
           |  |  | 169 |      *   pages work more quickly (works only for current user)
 | 
        
           |  |  | 170 |      * @param int $userid If set, specifies a different user ID to check availability for
 | 
        
           |  |  | 171 |      * @param \course_modinfo $modinfo Usually leave as null for default. Specify when
 | 
        
           |  |  | 172 |      *   calling recursively from inside get_fast_modinfo()
 | 
        
           |  |  | 173 |      * @return bool True if this item is available to the user, false otherwise
 | 
        
           |  |  | 174 |      */
 | 
        
           |  |  | 175 |     public function is_available(&$information, $grabthelot = false, $userid = 0,
 | 
        
           | 1441 | ariadna | 176 |             ?\course_modinfo $modinfo = null) {
 | 
        
           | 1 | efrain | 177 |         global $USER;
 | 
        
           |  |  | 178 |   | 
        
           |  |  | 179 |         // Default to no information.
 | 
        
           |  |  | 180 |         $information = '';
 | 
        
           |  |  | 181 |   | 
        
           |  |  | 182 |         // Do nothing if there are no availability restrictions.
 | 
        
           |  |  | 183 |         if (is_null($this->availability)) {
 | 
        
           |  |  | 184 |             return true;
 | 
        
           |  |  | 185 |         }
 | 
        
           |  |  | 186 |   | 
        
           |  |  | 187 |         // Resolve optional parameters.
 | 
        
           |  |  | 188 |         if (!$userid) {
 | 
        
           |  |  | 189 |             $userid = $USER->id;
 | 
        
           |  |  | 190 |         }
 | 
        
           |  |  | 191 |         if (!$modinfo) {
 | 
        
           |  |  | 192 |             $modinfo = get_fast_modinfo($this->course, $userid);
 | 
        
           |  |  | 193 |         }
 | 
        
           |  |  | 194 |         $this->modinfo = $modinfo;
 | 
        
           |  |  | 195 |   | 
        
           |  |  | 196 |         // Get availability from tree.
 | 
        
           |  |  | 197 |         try {
 | 
        
           |  |  | 198 |             $tree = $this->get_availability_tree();
 | 
        
           |  |  | 199 |             $result = $tree->check_available(false, $this, $grabthelot, $userid);
 | 
        
           |  |  | 200 |         } catch (\coding_exception $e) {
 | 
        
           |  |  | 201 |             $this->warn_about_invalid_availability($e);
 | 
        
           |  |  | 202 |             $this->modinfo = null;
 | 
        
           |  |  | 203 |             return false;
 | 
        
           |  |  | 204 |         }
 | 
        
           |  |  | 205 |   | 
        
           |  |  | 206 |         // See if there are any messages.
 | 
        
           |  |  | 207 |         if ($result->is_available()) {
 | 
        
           |  |  | 208 |             $this->modinfo = null;
 | 
        
           |  |  | 209 |             return true;
 | 
        
           |  |  | 210 |         } else {
 | 
        
           |  |  | 211 |             // If the item is marked as 'not visible' then we don't change the available
 | 
        
           |  |  | 212 |             // flag (visible/available are treated distinctly), but we remove any
 | 
        
           |  |  | 213 |             // availability info. If the item is hidden with the eye icon, it doesn't
 | 
        
           |  |  | 214 |             // make sense to show 'Available from <date>' or similar, because even
 | 
        
           |  |  | 215 |             // when that date arrives it will still not be available unless somebody
 | 
        
           |  |  | 216 |             // toggles the eye icon.
 | 
        
           |  |  | 217 |             if ($this->visible) {
 | 
        
           |  |  | 218 |                 $information = $tree->get_result_information($this, $result);
 | 
        
           |  |  | 219 |             }
 | 
        
           |  |  | 220 |   | 
        
           |  |  | 221 |             $this->modinfo = null;
 | 
        
           |  |  | 222 |             return false;
 | 
        
           |  |  | 223 |         }
 | 
        
           |  |  | 224 |     }
 | 
        
           |  |  | 225 |   | 
        
           |  |  | 226 |     /**
 | 
        
           |  |  | 227 |      * Checks whether this activity is going to be available for all users.
 | 
        
           |  |  | 228 |      *
 | 
        
           |  |  | 229 |      * Normally, if there are any conditions, then it may be hidden depending
 | 
        
           |  |  | 230 |      * on the user. However in the case of date conditions there are some
 | 
        
           |  |  | 231 |      * conditions which will definitely not result in it being hidden for
 | 
        
           |  |  | 232 |      * anyone.
 | 
        
           |  |  | 233 |      *
 | 
        
           |  |  | 234 |      * @return bool True if activity is available for all
 | 
        
           |  |  | 235 |      */
 | 
        
           |  |  | 236 |     public function is_available_for_all() {
 | 
        
           |  |  | 237 |         global $CFG;
 | 
        
           |  |  | 238 |         if (is_null($this->availability) || empty($CFG->enableavailability)) {
 | 
        
           |  |  | 239 |             return true;
 | 
        
           |  |  | 240 |         } else {
 | 
        
           |  |  | 241 |             try {
 | 
        
           |  |  | 242 |                 return $this->get_availability_tree()->is_available_for_all();
 | 
        
           |  |  | 243 |             } catch (\coding_exception $e) {
 | 
        
           |  |  | 244 |                 $this->warn_about_invalid_availability($e);
 | 
        
           |  |  | 245 |                 return false;
 | 
        
           |  |  | 246 |             }
 | 
        
           |  |  | 247 |         }
 | 
        
           |  |  | 248 |     }
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |     /**
 | 
        
           |  |  | 251 |      * Obtains a string describing all availability restrictions (even if
 | 
        
           |  |  | 252 |      * they do not apply any more). Used to display information for staff
 | 
        
           |  |  | 253 |      * editing the website.
 | 
        
           |  |  | 254 |      *
 | 
        
           |  |  | 255 |      * The modinfo parameter must be specified when it is called from inside
 | 
        
           |  |  | 256 |      * get_fast_modinfo, to avoid infinite recursion.
 | 
        
           |  |  | 257 |      *
 | 
        
           |  |  | 258 |      * This function displays debugging() messages if the availability
 | 
        
           |  |  | 259 |      * information is invalid.
 | 
        
           |  |  | 260 |      *
 | 
        
           |  |  | 261 |      * @param \course_modinfo $modinfo Usually leave as null for default
 | 
        
           |  |  | 262 |      * @return string Information string (for admin) about all restrictions on
 | 
        
           |  |  | 263 |      *   this item
 | 
        
           |  |  | 264 |      */
 | 
        
           | 1441 | ariadna | 265 |     public function get_full_information(?\course_modinfo $modinfo = null) {
 | 
        
           | 1 | efrain | 266 |         // Do nothing if there are no availability restrictions.
 | 
        
           |  |  | 267 |         if (is_null($this->availability)) {
 | 
        
           |  |  | 268 |             return '';
 | 
        
           |  |  | 269 |         }
 | 
        
           |  |  | 270 |   | 
        
           |  |  | 271 |         // Resolve optional parameter.
 | 
        
           |  |  | 272 |         if (!$modinfo) {
 | 
        
           |  |  | 273 |             $modinfo = get_fast_modinfo($this->course);
 | 
        
           |  |  | 274 |         }
 | 
        
           |  |  | 275 |         $this->modinfo = $modinfo;
 | 
        
           |  |  | 276 |   | 
        
           |  |  | 277 |         try {
 | 
        
           |  |  | 278 |             $result = $this->get_availability_tree()->get_full_information($this);
 | 
        
           |  |  | 279 |             $this->modinfo = null;
 | 
        
           |  |  | 280 |             return $result;
 | 
        
           |  |  | 281 |         } catch (\coding_exception $e) {
 | 
        
           |  |  | 282 |             $this->warn_about_invalid_availability($e);
 | 
        
           |  |  | 283 |             return false;
 | 
        
           |  |  | 284 |         }
 | 
        
           |  |  | 285 |     }
 | 
        
           |  |  | 286 |   | 
        
           |  |  | 287 |     /**
 | 
        
           |  |  | 288 |      * In some places we catch coding_exception because if a bug happens, it
 | 
        
           |  |  | 289 |      * would be fatal for the course page GUI; instead we just show a developer
 | 
        
           |  |  | 290 |      * debug message.
 | 
        
           |  |  | 291 |      *
 | 
        
           |  |  | 292 |      * @param \coding_exception $e Exception that occurred
 | 
        
           |  |  | 293 |      */
 | 
        
           |  |  | 294 |     protected function warn_about_invalid_availability(\coding_exception $e) {
 | 
        
           |  |  | 295 |         $name = $this->get_thing_name();
 | 
        
           |  |  | 296 |         $htmlname = $this->format_info($name, $this->course);
 | 
        
           |  |  | 297 |         // Because we call format_info here, likely in the middle of building dynamic data for the
 | 
        
           |  |  | 298 |         // activity, there could be a chance that the name might not be available.
 | 
        
           |  |  | 299 |         if ($htmlname === '') {
 | 
        
           |  |  | 300 |             // So instead use the numbers (cmid) from the tag.
 | 
        
           |  |  | 301 |             $htmlname = preg_replace('~[^0-9]~', '', $name);
 | 
        
           |  |  | 302 |         }
 | 
        
           |  |  | 303 |         $htmlname = html_to_text($htmlname, 75, false);
 | 
        
           |  |  | 304 |         $info = 'Error processing availability data for ‘' . $htmlname
 | 
        
           |  |  | 305 |                  . '’: ' . s($e->a);
 | 
        
           |  |  | 306 |         debugging($info, DEBUG_DEVELOPER);
 | 
        
           |  |  | 307 |     }
 | 
        
           |  |  | 308 |   | 
        
           |  |  | 309 |     /**
 | 
        
           |  |  | 310 |      * Called during restore (near end of restore). Updates any necessary ids
 | 
        
           |  |  | 311 |      * and writes the updated tree to the database. May output warnings if
 | 
        
           |  |  | 312 |      * necessary (e.g. if a course-module cannot be found after restore).
 | 
        
           |  |  | 313 |      *
 | 
        
           |  |  | 314 |      * @param string $restoreid Restore identifier
 | 
        
           |  |  | 315 |      * @param int $courseid Target course id
 | 
        
           |  |  | 316 |      * @param \base_logger $logger Logger for any warnings
 | 
        
           |  |  | 317 |      * @param int $dateoffset Date offset to be added to any dates (0 = none)
 | 
        
           |  |  | 318 |      * @param \base_task $task Restore task
 | 
        
           |  |  | 319 |      */
 | 
        
           |  |  | 320 |     public function update_after_restore($restoreid, $courseid, \base_logger $logger,
 | 
        
           |  |  | 321 |             $dateoffset, \base_task $task) {
 | 
        
           |  |  | 322 |         $tree = $this->get_availability_tree();
 | 
        
           |  |  | 323 |         // Set static data for use by get_restore_date_offset function.
 | 
        
           |  |  | 324 |         self::$restoreinfo = array('restoreid' => $restoreid, 'dateoffset' => $dateoffset,
 | 
        
           |  |  | 325 |                 'task' => $task);
 | 
        
           |  |  | 326 |         $changed = $tree->update_after_restore($restoreid, $courseid, $logger,
 | 
        
           |  |  | 327 |                 $this->get_thing_name());
 | 
        
           |  |  | 328 |         if ($changed) {
 | 
        
           |  |  | 329 |             // Save modified data.
 | 
        
           |  |  | 330 |             if ($tree->is_empty()) {
 | 
        
           |  |  | 331 |                 // If the tree is empty, but the tree has changed, remove this condition.
 | 
        
           |  |  | 332 |                 $this->set_in_database(null);
 | 
        
           |  |  | 333 |             } else {
 | 
        
           |  |  | 334 |                 $structure = $tree->save();
 | 
        
           |  |  | 335 |                 $this->set_in_database(json_encode($structure));
 | 
        
           |  |  | 336 |             }
 | 
        
           |  |  | 337 |         }
 | 
        
           |  |  | 338 |     }
 | 
        
           |  |  | 339 |   | 
        
           |  |  | 340 |     /**
 | 
        
           |  |  | 341 |      * Gets the date offset (amount by which any date values should be
 | 
        
           |  |  | 342 |      * adjusted) for the current restore.
 | 
        
           |  |  | 343 |      *
 | 
        
           |  |  | 344 |      * @param string $restoreid Restore identifier
 | 
        
           |  |  | 345 |      * @return int Date offset (0 if none)
 | 
        
           |  |  | 346 |      * @throws coding_exception If not in a restore (or not in that restore)
 | 
        
           |  |  | 347 |      */
 | 
        
           |  |  | 348 |     public static function get_restore_date_offset($restoreid) {
 | 
        
           |  |  | 349 |         if (!self::$restoreinfo) {
 | 
        
           |  |  | 350 |             throw new coding_exception('Only valid during restore');
 | 
        
           |  |  | 351 |         }
 | 
        
           |  |  | 352 |         if (self::$restoreinfo['restoreid'] !== $restoreid) {
 | 
        
           |  |  | 353 |             throw new coding_exception('Data not available for that restore id');
 | 
        
           |  |  | 354 |         }
 | 
        
           |  |  | 355 |         return self::$restoreinfo['dateoffset'];
 | 
        
           |  |  | 356 |     }
 | 
        
           |  |  | 357 |   | 
        
           |  |  | 358 |     /**
 | 
        
           |  |  | 359 |      * Gets the restore task (specifically, the task that calls the
 | 
        
           |  |  | 360 |      * update_after_restore method) for the current restore.
 | 
        
           |  |  | 361 |      *
 | 
        
           |  |  | 362 |      * @param string $restoreid Restore identifier
 | 
        
           |  |  | 363 |      * @return \base_task Restore task
 | 
        
           |  |  | 364 |      * @throws coding_exception If not in a restore (or not in that restore)
 | 
        
           |  |  | 365 |      */
 | 
        
           |  |  | 366 |     public static function get_restore_task($restoreid) {
 | 
        
           |  |  | 367 |         if (!self::$restoreinfo) {
 | 
        
           |  |  | 368 |             throw new coding_exception('Only valid during restore');
 | 
        
           |  |  | 369 |         }
 | 
        
           |  |  | 370 |         if (self::$restoreinfo['restoreid'] !== $restoreid) {
 | 
        
           |  |  | 371 |             throw new coding_exception('Data not available for that restore id');
 | 
        
           |  |  | 372 |         }
 | 
        
           |  |  | 373 |         return self::$restoreinfo['task'];
 | 
        
           |  |  | 374 |     }
 | 
        
           |  |  | 375 |   | 
        
           |  |  | 376 |     /**
 | 
        
           |  |  | 377 |      * Obtains the name of the item (cm_info or section_info, at present) that
 | 
        
           |  |  | 378 |      * this is controlling availability of. Name should be formatted ready
 | 
        
           |  |  | 379 |      * for on-screen display.
 | 
        
           |  |  | 380 |      *
 | 
        
           |  |  | 381 |      * @return string Name of item
 | 
        
           |  |  | 382 |      */
 | 
        
           |  |  | 383 |     abstract protected function get_thing_name();
 | 
        
           |  |  | 384 |   | 
        
           |  |  | 385 |     /**
 | 
        
           |  |  | 386 |      * Stores an updated availability tree JSON structure into the relevant
 | 
        
           |  |  | 387 |      * database table.
 | 
        
           |  |  | 388 |      *
 | 
        
           |  |  | 389 |      * @param string $availabilty New JSON value
 | 
        
           |  |  | 390 |      */
 | 
        
           |  |  | 391 |     abstract protected function set_in_database($availabilty);
 | 
        
           |  |  | 392 |   | 
        
           |  |  | 393 |     /**
 | 
        
           |  |  | 394 |      * In rare cases the system may want to change all references to one ID
 | 
        
           |  |  | 395 |      * (e.g. one course-module ID) to another one, within a course. This
 | 
        
           |  |  | 396 |      * function does that for the conditional availability data for all
 | 
        
           |  |  | 397 |      * modules and sections on the course.
 | 
        
           |  |  | 398 |      *
 | 
        
           |  |  | 399 |      * @param int|\stdClass $courseorid Course id or object
 | 
        
           |  |  | 400 |      * @param string $table Table name e.g. 'course_modules'
 | 
        
           |  |  | 401 |      * @param int $oldid Previous ID
 | 
        
           |  |  | 402 |      * @param int $newid New ID
 | 
        
           |  |  | 403 |      * @return bool True if anything changed, otherwise false
 | 
        
           |  |  | 404 |      */
 | 
        
           |  |  | 405 |     public static function update_dependency_id_across_course(
 | 
        
           |  |  | 406 |             $courseorid, $table, $oldid, $newid) {
 | 
        
           |  |  | 407 |         global $DB;
 | 
        
           |  |  | 408 |         $transaction = $DB->start_delegated_transaction();
 | 
        
           |  |  | 409 |         $modinfo = get_fast_modinfo($courseorid);
 | 
        
           |  |  | 410 |         $anychanged = false;
 | 
        
           |  |  | 411 |         foreach ($modinfo->get_cms() as $cm) {
 | 
        
           |  |  | 412 |             $info = new info_module($cm);
 | 
        
           |  |  | 413 |             $changed = $info->update_dependency_id($table, $oldid, $newid);
 | 
        
           |  |  | 414 |             $anychanged = $anychanged || $changed;
 | 
        
           |  |  | 415 |         }
 | 
        
           |  |  | 416 |         foreach ($modinfo->get_section_info_all() as $section) {
 | 
        
           |  |  | 417 |             $info = new info_section($section);
 | 
        
           |  |  | 418 |             $changed = $info->update_dependency_id($table, $oldid, $newid);
 | 
        
           |  |  | 419 |             $anychanged = $anychanged || $changed;
 | 
        
           |  |  | 420 |         }
 | 
        
           |  |  | 421 |         $transaction->allow_commit();
 | 
        
           |  |  | 422 |         if ($anychanged) {
 | 
        
           |  |  | 423 |             get_fast_modinfo($courseorid, 0, true);
 | 
        
           |  |  | 424 |         }
 | 
        
           |  |  | 425 |         return $anychanged;
 | 
        
           |  |  | 426 |     }
 | 
        
           |  |  | 427 |   | 
        
           |  |  | 428 |     /**
 | 
        
           |  |  | 429 |      * Called on a single item. If necessary, updates availability data where
 | 
        
           |  |  | 430 |      * it has a dependency on an item with a particular id.
 | 
        
           |  |  | 431 |      *
 | 
        
           |  |  | 432 |      * @param string $table Table name e.g. 'course_modules'
 | 
        
           |  |  | 433 |      * @param int $oldid Previous ID
 | 
        
           |  |  | 434 |      * @param int $newid New ID
 | 
        
           |  |  | 435 |      * @return bool True if it changed, otherwise false
 | 
        
           |  |  | 436 |      */
 | 
        
           |  |  | 437 |     protected function update_dependency_id($table, $oldid, $newid) {
 | 
        
           |  |  | 438 |         // Do nothing if there are no availability restrictions.
 | 
        
           |  |  | 439 |         if (is_null($this->availability)) {
 | 
        
           |  |  | 440 |             return false;
 | 
        
           |  |  | 441 |         }
 | 
        
           |  |  | 442 |         // Pass requirement on to tree object.
 | 
        
           |  |  | 443 |         $tree = $this->get_availability_tree();
 | 
        
           |  |  | 444 |         $changed = $tree->update_dependency_id($table, $oldid, $newid);
 | 
        
           |  |  | 445 |         if ($changed) {
 | 
        
           |  |  | 446 |             // Save modified data.
 | 
        
           |  |  | 447 |             $structure = $tree->save();
 | 
        
           |  |  | 448 |             $this->set_in_database(json_encode($structure));
 | 
        
           |  |  | 449 |         }
 | 
        
           |  |  | 450 |         return $changed;
 | 
        
           |  |  | 451 |     }
 | 
        
           |  |  | 452 |   | 
        
           |  |  | 453 |     /**
 | 
        
           |  |  | 454 |      * Converts legacy data from fields (if provided) into the new availability
 | 
        
           |  |  | 455 |      * syntax.
 | 
        
           |  |  | 456 |      *
 | 
        
           |  |  | 457 |      * Supported fields: availablefrom, availableuntil, showavailability
 | 
        
           |  |  | 458 |      * (and groupingid for sections).
 | 
        
           |  |  | 459 |      *
 | 
        
           |  |  | 460 |      * It also supports the groupmembersonly field for modules. This part was
 | 
        
           |  |  | 461 |      * optional in 2.7 but now always runs (because groupmembersonly has been
 | 
        
           |  |  | 462 |      * removed).
 | 
        
           |  |  | 463 |      *
 | 
        
           |  |  | 464 |      * @param \stdClass $rec Object possibly containing legacy fields
 | 
        
           |  |  | 465 |      * @param bool $section True if this is a section
 | 
        
           |  |  | 466 |      * @param bool $modgroupmembersonlyignored Ignored option, previously used
 | 
        
           |  |  | 467 |      * @return string|null New availability value or null if none
 | 
        
           |  |  | 468 |      */
 | 
        
           |  |  | 469 |     public static function convert_legacy_fields($rec, $section, $modgroupmembersonlyignored = false) {
 | 
        
           |  |  | 470 |         // Do nothing if the fields are not set.
 | 
        
           |  |  | 471 |         if (empty($rec->availablefrom) && empty($rec->availableuntil) &&
 | 
        
           |  |  | 472 |                 (empty($rec->groupmembersonly)) &&
 | 
        
           |  |  | 473 |                 (!$section || empty($rec->groupingid))) {
 | 
        
           |  |  | 474 |             return null;
 | 
        
           |  |  | 475 |         }
 | 
        
           |  |  | 476 |   | 
        
           |  |  | 477 |         // Handle legacy availability data.
 | 
        
           |  |  | 478 |         $conditions = array();
 | 
        
           |  |  | 479 |         $shows = array();
 | 
        
           |  |  | 480 |   | 
        
           |  |  | 481 |         // Groupmembersonly condition (if enabled) for modules, groupingid for
 | 
        
           |  |  | 482 |         // sections.
 | 
        
           |  |  | 483 |         if (!empty($rec->groupmembersonly) ||
 | 
        
           |  |  | 484 |                 (!empty($rec->groupingid) && $section)) {
 | 
        
           |  |  | 485 |             if (!empty($rec->groupingid)) {
 | 
        
           |  |  | 486 |                 $conditions[] = '{"type":"grouping"' .
 | 
        
           |  |  | 487 |                         ($rec->groupingid ? ',"id":' . $rec->groupingid : '') . '}';
 | 
        
           |  |  | 488 |             } else {
 | 
        
           |  |  | 489 |                 // No grouping specified, so allow any group.
 | 
        
           |  |  | 490 |                 $conditions[] = '{"type":"group"}';
 | 
        
           |  |  | 491 |             }
 | 
        
           |  |  | 492 |             // Group members only condition was not displayed to students.
 | 
        
           |  |  | 493 |             $shows[] = 'false';
 | 
        
           |  |  | 494 |         }
 | 
        
           |  |  | 495 |   | 
        
           |  |  | 496 |         // Date conditions.
 | 
        
           |  |  | 497 |         if (!empty($rec->availablefrom)) {
 | 
        
           |  |  | 498 |             $conditions[] = '{"type":"date","d":">=","t":' . $rec->availablefrom . '}';
 | 
        
           |  |  | 499 |             $shows[] = !empty($rec->showavailability) ? 'true' : 'false';
 | 
        
           |  |  | 500 |         }
 | 
        
           |  |  | 501 |         if (!empty($rec->availableuntil)) {
 | 
        
           |  |  | 502 |             $conditions[] = '{"type":"date","d":"<","t":' . $rec->availableuntil . '}';
 | 
        
           |  |  | 503 |             // Until dates never showed to students.
 | 
        
           |  |  | 504 |             $shows[] = 'false';
 | 
        
           |  |  | 505 |         }
 | 
        
           |  |  | 506 |   | 
        
           |  |  | 507 |         // If there are some conditions, return them.
 | 
        
           |  |  | 508 |         if ($conditions) {
 | 
        
           |  |  | 509 |             return '{"op":"&","showc":[' . implode(',', $shows) . '],' .
 | 
        
           |  |  | 510 |                     '"c":[' . implode(',', $conditions) . ']}';
 | 
        
           |  |  | 511 |         } else {
 | 
        
           |  |  | 512 |             return null;
 | 
        
           |  |  | 513 |         }
 | 
        
           |  |  | 514 |     }
 | 
        
           |  |  | 515 |   | 
        
           |  |  | 516 |     /**
 | 
        
           |  |  | 517 |      * Adds a condition from the legacy availability condition.
 | 
        
           |  |  | 518 |      *
 | 
        
           |  |  | 519 |      * (For use during restore only.)
 | 
        
           |  |  | 520 |      *
 | 
        
           |  |  | 521 |      * This function assumes that the activity either has no conditions, or
 | 
        
           |  |  | 522 |      * that it has an AND tree with one or more conditions.
 | 
        
           |  |  | 523 |      *
 | 
        
           |  |  | 524 |      * @param string|null $availability Current availability conditions
 | 
        
           |  |  | 525 |      * @param \stdClass $rec Object containing information from old table
 | 
        
           |  |  | 526 |      * @param bool $show True if 'show' option should be enabled
 | 
        
           |  |  | 527 |      * @return string New availability conditions
 | 
        
           |  |  | 528 |      */
 | 
        
           |  |  | 529 |     public static function add_legacy_availability_condition($availability, $rec, $show) {
 | 
        
           |  |  | 530 |         if (!empty($rec->sourcecmid)) {
 | 
        
           |  |  | 531 |             // Completion condition.
 | 
        
           |  |  | 532 |             $condition = '{"type":"completion","cm":' . $rec->sourcecmid .
 | 
        
           |  |  | 533 |                     ',"e":' . $rec->requiredcompletion . '}';
 | 
        
           |  |  | 534 |         } else {
 | 
        
           |  |  | 535 |             // Grade condition.
 | 
        
           |  |  | 536 |             $minmax = '';
 | 
        
           |  |  | 537 |             if (!empty($rec->grademin)) {
 | 
        
           |  |  | 538 |                 $minmax .= ',"min":' . sprintf('%.5f', $rec->grademin);
 | 
        
           |  |  | 539 |             }
 | 
        
           |  |  | 540 |             if (!empty($rec->grademax)) {
 | 
        
           |  |  | 541 |                 $minmax .= ',"max":' . sprintf('%.5f', $rec->grademax);
 | 
        
           |  |  | 542 |             }
 | 
        
           |  |  | 543 |             $condition = '{"type":"grade","id":' . $rec->gradeitemid . $minmax . '}';
 | 
        
           |  |  | 544 |         }
 | 
        
           |  |  | 545 |   | 
        
           |  |  | 546 |         return self::add_legacy_condition($availability, $condition, $show);
 | 
        
           |  |  | 547 |     }
 | 
        
           |  |  | 548 |   | 
        
           |  |  | 549 |     /**
 | 
        
           |  |  | 550 |      * Adds a condition from the legacy availability field condition.
 | 
        
           |  |  | 551 |      *
 | 
        
           |  |  | 552 |      * (For use during restore only.)
 | 
        
           |  |  | 553 |      *
 | 
        
           |  |  | 554 |      * This function assumes that the activity either has no conditions, or
 | 
        
           |  |  | 555 |      * that it has an AND tree with one or more conditions.
 | 
        
           |  |  | 556 |      *
 | 
        
           |  |  | 557 |      * @param string|null $availability Current availability conditions
 | 
        
           |  |  | 558 |      * @param \stdClass $rec Object containing information from old table
 | 
        
           |  |  | 559 |      * @param bool $show True if 'show' option should be enabled
 | 
        
           |  |  | 560 |      * @return string New availability conditions
 | 
        
           |  |  | 561 |      */
 | 
        
           |  |  | 562 |     public static function add_legacy_availability_field_condition($availability, $rec, $show) {
 | 
        
           |  |  | 563 |         if (isset($rec->userfield)) {
 | 
        
           |  |  | 564 |             // Standard field.
 | 
        
           |  |  | 565 |             $fieldbit = ',"sf":' . json_encode($rec->userfield);
 | 
        
           |  |  | 566 |         } else {
 | 
        
           |  |  | 567 |             // Custom field.
 | 
        
           |  |  | 568 |             $fieldbit = ',"cf":' . json_encode($rec->shortname);
 | 
        
           |  |  | 569 |         }
 | 
        
           |  |  | 570 |         // Value is not included for certain operators.
 | 
        
           |  |  | 571 |         switch($rec->operator) {
 | 
        
           |  |  | 572 |             case 'isempty':
 | 
        
           |  |  | 573 |             case 'isnotempty':
 | 
        
           |  |  | 574 |                 $valuebit = '';
 | 
        
           |  |  | 575 |                 break;
 | 
        
           |  |  | 576 |   | 
        
           |  |  | 577 |             default:
 | 
        
           |  |  | 578 |                 $valuebit = ',"v":' . json_encode($rec->value);
 | 
        
           |  |  | 579 |                 break;
 | 
        
           |  |  | 580 |         }
 | 
        
           |  |  | 581 |         $condition = '{"type":"profile","op":"' . $rec->operator . '"' .
 | 
        
           |  |  | 582 |                 $fieldbit . $valuebit . '}';
 | 
        
           |  |  | 583 |   | 
        
           |  |  | 584 |         return self::add_legacy_condition($availability, $condition, $show);
 | 
        
           |  |  | 585 |     }
 | 
        
           |  |  | 586 |   | 
        
           |  |  | 587 |     /**
 | 
        
           |  |  | 588 |      * Adds a condition to an AND group.
 | 
        
           |  |  | 589 |      *
 | 
        
           |  |  | 590 |      * (For use during restore only.)
 | 
        
           |  |  | 591 |      *
 | 
        
           |  |  | 592 |      * This function assumes that the activity either has no conditions, or
 | 
        
           |  |  | 593 |      * that it has only conditions added by this function.
 | 
        
           |  |  | 594 |      *
 | 
        
           |  |  | 595 |      * @param string|null $availability Current availability conditions
 | 
        
           |  |  | 596 |      * @param string $condition Condition text '{...}'
 | 
        
           |  |  | 597 |      * @param bool $show True if 'show' option should be enabled
 | 
        
           |  |  | 598 |      * @return string New availability conditions
 | 
        
           |  |  | 599 |      */
 | 
        
           |  |  | 600 |     protected static function add_legacy_condition($availability, $condition, $show) {
 | 
        
           |  |  | 601 |         $showtext = ($show ? 'true' : 'false');
 | 
        
           |  |  | 602 |         if (is_null($availability)) {
 | 
        
           |  |  | 603 |             $availability = '{"op":"&","showc":[' . $showtext .
 | 
        
           |  |  | 604 |                     '],"c":[' . $condition . ']}';
 | 
        
           |  |  | 605 |         } else {
 | 
        
           |  |  | 606 |             $matches = array();
 | 
        
           |  |  | 607 |             if (!preg_match('~^({"op":"&","showc":\[(?:true|false)(?:,(?:true|false))*)' .
 | 
        
           |  |  | 608 |                     '(\],"c":\[.*)(\]})$~', $availability, $matches)) {
 | 
        
           |  |  | 609 |                 throw new \coding_exception('Unexpected availability value');
 | 
        
           |  |  | 610 |             }
 | 
        
           |  |  | 611 |             $availability = $matches[1] . ',' . $showtext . $matches[2] .
 | 
        
           |  |  | 612 |                     ',' . $condition . $matches[3];
 | 
        
           |  |  | 613 |         }
 | 
        
           |  |  | 614 |         return $availability;
 | 
        
           |  |  | 615 |     }
 | 
        
           |  |  | 616 |   | 
        
           |  |  | 617 |     /**
 | 
        
           |  |  | 618 |      * Tests against a user list. Users who cannot access the activity due to
 | 
        
           |  |  | 619 |      * availability restrictions will be removed from the list.
 | 
        
           |  |  | 620 |      *
 | 
        
           |  |  | 621 |      * Note this only includes availability restrictions (those handled within
 | 
        
           |  |  | 622 |      * this API) and not other ways of restricting access.
 | 
        
           |  |  | 623 |      *
 | 
        
           |  |  | 624 |      * This test ONLY includes conditions which are marked as being applied to
 | 
        
           |  |  | 625 |      * user lists. For example, group conditions are included but date
 | 
        
           |  |  | 626 |      * conditions are not included.
 | 
        
           |  |  | 627 |      *
 | 
        
           |  |  | 628 |      * The function operates reasonably efficiently i.e. should not do per-user
 | 
        
           |  |  | 629 |      * database queries. It is however likely to be fairly slow.
 | 
        
           |  |  | 630 |      *
 | 
        
           |  |  | 631 |      * @param array $users Array of userid => object
 | 
        
           |  |  | 632 |      * @return array Filtered version of input array
 | 
        
           |  |  | 633 |      */
 | 
        
           |  |  | 634 |     public function filter_user_list(array $users) {
 | 
        
           |  |  | 635 |         global $CFG;
 | 
        
           |  |  | 636 |         if (is_null($this->availability) || !$CFG->enableavailability) {
 | 
        
           |  |  | 637 |             return $users;
 | 
        
           |  |  | 638 |         }
 | 
        
           |  |  | 639 |         $tree = $this->get_availability_tree();
 | 
        
           |  |  | 640 |         $checker = new capability_checker($this->get_context());
 | 
        
           |  |  | 641 |   | 
        
           |  |  | 642 |         // Filter using availability tree.
 | 
        
           |  |  | 643 |         $this->modinfo = get_fast_modinfo($this->get_course());
 | 
        
           |  |  | 644 |         $filtered = $tree->filter_user_list($users, false, $this, $checker);
 | 
        
           |  |  | 645 |         $this->modinfo = null;
 | 
        
           |  |  | 646 |   | 
        
           |  |  | 647 |         // Include users in the result if they're either in the filtered list,
 | 
        
           |  |  | 648 |         // or they have viewhidden. This logic preserves ordering of the
 | 
        
           |  |  | 649 |         // passed users array.
 | 
        
           |  |  | 650 |         $result = array();
 | 
        
           |  |  | 651 |         $canviewhidden = $checker->get_users_by_capability($this->get_view_hidden_capability());
 | 
        
           |  |  | 652 |         foreach ($users as $userid => $data) {
 | 
        
           |  |  | 653 |             if (array_key_exists($userid, $filtered) || array_key_exists($userid, $canviewhidden)) {
 | 
        
           |  |  | 654 |                 $result[$userid] = $users[$userid];
 | 
        
           |  |  | 655 |             }
 | 
        
           |  |  | 656 |         }
 | 
        
           |  |  | 657 |   | 
        
           |  |  | 658 |         return $result;
 | 
        
           |  |  | 659 |     }
 | 
        
           |  |  | 660 |   | 
        
           |  |  | 661 |     /**
 | 
        
           |  |  | 662 |      * Gets the capability used to view hidden activities/sections (as
 | 
        
           |  |  | 663 |      * appropriate).
 | 
        
           |  |  | 664 |      *
 | 
        
           |  |  | 665 |      * @return string Name of capability used to view hidden items of this type
 | 
        
           |  |  | 666 |      */
 | 
        
           |  |  | 667 |     abstract protected function get_view_hidden_capability();
 | 
        
           |  |  | 668 |   | 
        
           |  |  | 669 |     /**
 | 
        
           |  |  | 670 |      * Obtains SQL that returns a list of enrolled users that has been filtered
 | 
        
           |  |  | 671 |      * by the conditions applied in the availability API, similar to calling
 | 
        
           |  |  | 672 |      * get_enrolled_users and then filter_user_list. As for filter_user_list,
 | 
        
           |  |  | 673 |      * this ONLY filters out users with conditions that are marked as applying
 | 
        
           |  |  | 674 |      * to user lists. For example, group conditions are included but date
 | 
        
           |  |  | 675 |      * conditions are not included.
 | 
        
           |  |  | 676 |      *
 | 
        
           |  |  | 677 |      * The returned SQL is a query that returns a list of user IDs. It does not
 | 
        
           |  |  | 678 |      * include brackets, so you neeed to add these to make it into a subquery.
 | 
        
           |  |  | 679 |      * You would normally use it in an SQL phrase like "WHERE u.id IN ($sql)".
 | 
        
           |  |  | 680 |      *
 | 
        
           |  |  | 681 |      * The function returns an array with '' and an empty array, if there are
 | 
        
           |  |  | 682 |      * no restrictions on users from these conditions.
 | 
        
           |  |  | 683 |      *
 | 
        
           |  |  | 684 |      * The SQL will be complex and may be slow. It uses named parameters (sorry,
 | 
        
           |  |  | 685 |      * I know they are annoying, but it was unavoidable here).
 | 
        
           |  |  | 686 |      *
 | 
        
           |  |  | 687 |      * @param bool $onlyactive True if including only active enrolments
 | 
        
           |  |  | 688 |      * @return array Array of SQL code (may be empty) and params
 | 
        
           |  |  | 689 |      */
 | 
        
           |  |  | 690 |     public function get_user_list_sql($onlyactive) {
 | 
        
           |  |  | 691 |         global $CFG;
 | 
        
           |  |  | 692 |         if (is_null($this->availability) || !$CFG->enableavailability) {
 | 
        
           |  |  | 693 |             return array('', array());
 | 
        
           |  |  | 694 |         }
 | 
        
           |  |  | 695 |   | 
        
           |  |  | 696 |         // Get SQL for the availability filter.
 | 
        
           |  |  | 697 |         $tree = $this->get_availability_tree();
 | 
        
           |  |  | 698 |         list ($filtersql, $filterparams) = $tree->get_user_list_sql(false, $this, $onlyactive);
 | 
        
           |  |  | 699 |         if ($filtersql === '') {
 | 
        
           |  |  | 700 |             // No restrictions, so return empty query.
 | 
        
           |  |  | 701 |             return array('', array());
 | 
        
           |  |  | 702 |         }
 | 
        
           |  |  | 703 |   | 
        
           |  |  | 704 |         // Get SQL for the view hidden list.
 | 
        
           |  |  | 705 |         list ($viewhiddensql, $viewhiddenparams) = get_enrolled_sql(
 | 
        
           |  |  | 706 |                 $this->get_context(), $this->get_view_hidden_capability(), 0, $onlyactive);
 | 
        
           |  |  | 707 |   | 
        
           |  |  | 708 |         // Result is a union of the two.
 | 
        
           |  |  | 709 |         return array('(' . $filtersql . ') UNION (' . $viewhiddensql . ')',
 | 
        
           |  |  | 710 |                 array_merge($filterparams, $viewhiddenparams));
 | 
        
           |  |  | 711 |     }
 | 
        
           |  |  | 712 |   | 
        
           |  |  | 713 |     /**
 | 
        
           |  |  | 714 |      * Formats the $cm->availableinfo string for display. This includes
 | 
        
           |  |  | 715 |      * filling in the names of any course-modules that might be mentioned.
 | 
        
           |  |  | 716 |      * Should be called immediately prior to display, or at least somewhere
 | 
        
           |  |  | 717 |      * that we can guarantee does not happen from within building the modinfo
 | 
        
           |  |  | 718 |      * object.
 | 
        
           |  |  | 719 |      *
 | 
        
           |  |  | 720 |      * @param \renderable|string $inforenderable Info string or renderable
 | 
        
           |  |  | 721 |      * @param int|\stdClass $courseorid
 | 
        
           |  |  | 722 |      * @return string Correctly formatted info string
 | 
        
           |  |  | 723 |      */
 | 
        
           |  |  | 724 |     public static function format_info($inforenderable, $courseorid) {
 | 
        
           |  |  | 725 |         global $PAGE, $OUTPUT;
 | 
        
           |  |  | 726 |   | 
        
           |  |  | 727 |         // Use renderer if required.
 | 
        
           |  |  | 728 |         if (is_string($inforenderable)) {
 | 
        
           |  |  | 729 |             $info = $inforenderable;
 | 
        
           |  |  | 730 |         } else {
 | 
        
           |  |  | 731 |             $renderable = new \core_availability\output\availability_info($inforenderable);
 | 
        
           |  |  | 732 |             $info = $OUTPUT->render($renderable);
 | 
        
           |  |  | 733 |         }
 | 
        
           |  |  | 734 |   | 
        
           |  |  | 735 |         // Don't waste time if there are no special tags.
 | 
        
           |  |  | 736 |         if (strpos($info, '<AVAILABILITY_') === false) {
 | 
        
           |  |  | 737 |             return $info;
 | 
        
           |  |  | 738 |         }
 | 
        
           |  |  | 739 |   | 
        
           |  |  | 740 |         // Handle CMNAME tags.
 | 
        
           |  |  | 741 |         $modinfo = get_fast_modinfo($courseorid);
 | 
        
           |  |  | 742 |         $context = \context_course::instance($modinfo->courseid);
 | 
        
           |  |  | 743 |         $info = preg_replace_callback('~<AVAILABILITY_CMNAME_([0-9]+)/>~',
 | 
        
           |  |  | 744 |                 function($matches) use($modinfo, $context) {
 | 
        
           |  |  | 745 |                     $cm = $modinfo->get_cm($matches[1]);
 | 
        
           |  |  | 746 |                     $modulename = format_string($cm->get_name(), true, ['context' => $context]);
 | 
        
           |  |  | 747 |                     // We make sure that we add a data attribute to the name so we can change it later if the
 | 
        
           |  |  | 748 |                     // original module name changes.
 | 
        
           |  |  | 749 |                     if ($cm->has_view() && $cm->get_user_visible()) {
 | 
        
           |  |  | 750 |                         // Help student by providing a link to the module which is preventing availability.
 | 
        
           |  |  | 751 |                         return \html_writer::link($cm->get_url(), $modulename, ['data-cm-name-for' => $cm->id]);
 | 
        
           |  |  | 752 |                     } else {
 | 
        
           |  |  | 753 |                         return \html_writer::span($modulename, '', ['data-cm-name-for' => $cm->id]);
 | 
        
           |  |  | 754 |                     }
 | 
        
           |  |  | 755 |                 }, $info);
 | 
        
           |  |  | 756 |         $info = preg_replace_callback('~<AVAILABILITY_FORMAT_STRING>(.*?)</AVAILABILITY_FORMAT_STRING>~s',
 | 
        
           |  |  | 757 |                 function($matches) use ($context) {
 | 
        
           |  |  | 758 |                     $decoded = htmlspecialchars_decode($matches[1], ENT_NOQUOTES);
 | 
        
           |  |  | 759 |                     return format_string($decoded, true, ['context' => $context]);
 | 
        
           |  |  | 760 |                 }, $info);
 | 
        
           |  |  | 761 |         $info = preg_replace_callback('~<AVAILABILITY_CALLBACK type="([a-z0-9_]+)">(.*?)</AVAILABILITY_CALLBACK>~s',
 | 
        
           |  |  | 762 |                 function($matches) use ($modinfo, $context) {
 | 
        
           |  |  | 763 |                     // Find the class, it must have already been loaded by now.
 | 
        
           |  |  | 764 |                     $fullclassname = 'availability_' . $matches[1] . '\condition';
 | 
        
           |  |  | 765 |                     if (!class_exists($fullclassname, false)) {
 | 
        
           |  |  | 766 |                         return '<!-- Error finding class ' . $fullclassname .' -->';
 | 
        
           |  |  | 767 |                     }
 | 
        
           |  |  | 768 |                     // Load the parameters.
 | 
        
           |  |  | 769 |                     $params = [];
 | 
        
           |  |  | 770 |                     $encodedparams = preg_split('~<P/>~', $matches[2], 0);
 | 
        
           |  |  | 771 |                     foreach ($encodedparams as $encodedparam) {
 | 
        
           |  |  | 772 |                         $params[] = htmlspecialchars_decode($encodedparam, ENT_NOQUOTES);
 | 
        
           |  |  | 773 |                     }
 | 
        
           |  |  | 774 |                     return $fullclassname::get_description_callback_value($modinfo, $context, $params);
 | 
        
           |  |  | 775 |                 }, $info);
 | 
        
           |  |  | 776 |   | 
        
           |  |  | 777 |         return $info;
 | 
        
           |  |  | 778 |     }
 | 
        
           |  |  | 779 |   | 
        
           |  |  | 780 |     /**
 | 
        
           |  |  | 781 |      * Used in course/lib.php because we need to disable the completion tickbox
 | 
        
           |  |  | 782 |      * JS (using the non-JS version instead, which causes a page reload) if a
 | 
        
           |  |  | 783 |      * completion tickbox value may affect a conditional activity.
 | 
        
           |  |  | 784 |      *
 | 
        
           |  |  | 785 |      * @param \stdClass $course Moodle course object
 | 
        
           |  |  | 786 |      * @param int $cmid Course-module id
 | 
        
           |  |  | 787 |      * @return bool True if this is used in a condition, false otherwise
 | 
        
           |  |  | 788 |      */
 | 
        
           |  |  | 789 |     public static function completion_value_used($course, $cmid) {
 | 
        
           |  |  | 790 |         // Access all plugins. Normally only the completion plugin is going
 | 
        
           |  |  | 791 |         // to affect this value, but it's potentially possible that some other
 | 
        
           |  |  | 792 |         // plugin could also rely on the completion plugin.
 | 
        
           |  |  | 793 |         $pluginmanager = \core_plugin_manager::instance();
 | 
        
           |  |  | 794 |         $enabled = $pluginmanager->get_enabled_plugins('availability');
 | 
        
           |  |  | 795 |         foreach ($enabled as $plugin => $info) {
 | 
        
           | 1441 | ariadna | 796 |             /** @var \core_availability\condition $class */
 | 
        
           | 1 | efrain | 797 |             $class = '\availability_' . $plugin . '\condition';
 | 
        
           | 1441 | ariadna | 798 |             if (class_exists($class) && $class::completion_value_used($course, $cmid)) {
 | 
        
           | 1 | efrain | 799 |                 return true;
 | 
        
           |  |  | 800 |             }
 | 
        
           |  |  | 801 |         }
 | 
        
           |  |  | 802 |         return false;
 | 
        
           |  |  | 803 |     }
 | 
        
           |  |  | 804 |   | 
        
           |  |  | 805 |     /**
 | 
        
           |  |  | 806 |      * Returns groups that the given user belongs to on the course. Note: If not already
 | 
        
           |  |  | 807 |      * available, this may make a database query.
 | 
        
           |  |  | 808 |      *
 | 
        
           |  |  | 809 |      * This will include groups the user is not allowed to see themselves, so check visibility
 | 
        
           |  |  | 810 |      * before displaying groups to the user.
 | 
        
           |  |  | 811 |      *
 | 
        
           |  |  | 812 |      * @param int $groupingid Grouping ID or 0 (default) for all groups
 | 
        
           |  |  | 813 |      * @param int $userid User ID or 0 (default) for current user
 | 
        
           |  |  | 814 |      * @return int[] Array of int (group id) => int (same group id again); empty array if none
 | 
        
           |  |  | 815 |      */
 | 
        
           |  |  | 816 |     public function get_groups(int $groupingid = 0, int $userid = 0): array {
 | 
        
           |  |  | 817 |         global $USER;
 | 
        
           |  |  | 818 |         if (empty($userid)) {
 | 
        
           |  |  | 819 |             $userid = $USER->id;
 | 
        
           |  |  | 820 |         }
 | 
        
           |  |  | 821 |         if (!array_key_exists($userid, $this->groups)) {
 | 
        
           |  |  | 822 |             $allgroups = groups_get_user_groups($this->course->id, $userid, true);
 | 
        
           |  |  | 823 |             $this->groups[$userid] = $allgroups;
 | 
        
           |  |  | 824 |         } else {
 | 
        
           |  |  | 825 |             $allgroups = $this->groups[$userid];
 | 
        
           |  |  | 826 |         }
 | 
        
           |  |  | 827 |         if (!isset($allgroups[$groupingid])) {
 | 
        
           |  |  | 828 |             return [];
 | 
        
           |  |  | 829 |         }
 | 
        
           |  |  | 830 |         return $allgroups[$groupingid];
 | 
        
           |  |  | 831 |     }
 | 
        
           |  |  | 832 | }
 |