| 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 |  * Activity completion condition.
 | 
        
           |  |  | 19 |  *
 | 
        
           |  |  | 20 |  * @package availability_completion
 | 
        
           |  |  | 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_completion;
 | 
        
           |  |  | 26 |   | 
        
           |  |  | 27 | use cache;
 | 
        
           |  |  | 28 | use core_availability\info;
 | 
        
           |  |  | 29 | use core_availability\info_module;
 | 
        
           |  |  | 30 | use core_availability\info_section;
 | 
        
           |  |  | 31 | use stdClass;
 | 
        
           |  |  | 32 |   | 
        
           |  |  | 33 | defined('MOODLE_INTERNAL') || die();
 | 
        
           |  |  | 34 |   | 
        
           |  |  | 35 | require_once($CFG->libdir . '/completionlib.php');
 | 
        
           |  |  | 36 |   | 
        
           |  |  | 37 | /**
 | 
        
           |  |  | 38 |  * Activity completion condition.
 | 
        
           |  |  | 39 |  *
 | 
        
           |  |  | 40 |  * @package availability_completion
 | 
        
           |  |  | 41 |  * @copyright 2014 The Open University
 | 
        
           |  |  | 42 |  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 43 |  */
 | 
        
           |  |  | 44 | class condition extends \core_availability\condition {
 | 
        
           |  |  | 45 |   | 
        
           |  |  | 46 |     /** @var int previous module cm value used to calculate relative completions */
 | 
        
           |  |  | 47 |     public const OPTION_PREVIOUS = -1;
 | 
        
           |  |  | 48 |   | 
        
           |  |  | 49 |     /** @var int ID of module that this depends on */
 | 
        
           |  |  | 50 |     protected $cmid;
 | 
        
           |  |  | 51 |   | 
        
           |  |  | 52 |     /** @var array IDs of the current module and section */
 | 
        
           |  |  | 53 |     protected $selfids;
 | 
        
           |  |  | 54 |   | 
        
           |  |  | 55 |     /** @var int Expected completion type (one of the COMPLETE_xx constants) */
 | 
        
           |  |  | 56 |     protected $expectedcompletion;
 | 
        
           |  |  | 57 |   | 
        
           |  |  | 58 |     /** @var array Array of previous cmids used to calculate relative completions */
 | 
        
           |  |  | 59 |     protected $modfastprevious = [];
 | 
        
           |  |  | 60 |   | 
        
           |  |  | 61 |     /** @var array Array of cmids previous to each course section */
 | 
        
           |  |  | 62 |     protected $sectionfastprevious = [];
 | 
        
           |  |  | 63 |   | 
        
           |  |  | 64 |     /** @var array Array of modules used in these conditions for course */
 | 
        
           |  |  | 65 |     protected static $modsusedincondition = [];
 | 
        
           |  |  | 66 |   | 
        
           |  |  | 67 |     /**
 | 
        
           |  |  | 68 |      * Constructor.
 | 
        
           |  |  | 69 |      *
 | 
        
           |  |  | 70 |      * @param \stdClass $structure Data structure from JSON decode
 | 
        
           |  |  | 71 |      * @throws \coding_exception If invalid data structure.
 | 
        
           |  |  | 72 |      */
 | 
        
           |  |  | 73 |     public function __construct($structure) {
 | 
        
           |  |  | 74 |         // Get cmid.
 | 
        
           |  |  | 75 |         if (isset($structure->cm) && is_number($structure->cm)) {
 | 
        
           |  |  | 76 |             $this->cmid = (int)$structure->cm;
 | 
        
           |  |  | 77 |         } else {
 | 
        
           |  |  | 78 |             throw new \coding_exception('Missing or invalid ->cm for completion condition');
 | 
        
           |  |  | 79 |         }
 | 
        
           |  |  | 80 |         // Get expected completion.
 | 
        
           |  |  | 81 |         if (isset($structure->e) && in_array($structure->e,
 | 
        
           |  |  | 82 |                 [COMPLETION_COMPLETE, COMPLETION_INCOMPLETE,
 | 
        
           |  |  | 83 |                 COMPLETION_COMPLETE_PASS, COMPLETION_COMPLETE_FAIL])) {
 | 
        
           |  |  | 84 |             $this->expectedcompletion = $structure->e;
 | 
        
           |  |  | 85 |         } else {
 | 
        
           |  |  | 86 |             throw new \coding_exception('Missing or invalid ->e for completion condition');
 | 
        
           |  |  | 87 |         }
 | 
        
           |  |  | 88 |     }
 | 
        
           |  |  | 89 |   | 
        
           |  |  | 90 |     /**
 | 
        
           |  |  | 91 |      * Saves tree data back to a structure object.
 | 
        
           |  |  | 92 |      *
 | 
        
           |  |  | 93 |      * @return stdClass Structure object (ready to be made into JSON format)
 | 
        
           |  |  | 94 |      */
 | 
        
           |  |  | 95 |     public function save(): stdClass {
 | 
        
           |  |  | 96 |         return (object) [
 | 
        
           |  |  | 97 |             'type' => 'completion',
 | 
        
           |  |  | 98 |             'cm' => $this->cmid,
 | 
        
           |  |  | 99 |             'e' => $this->expectedcompletion,
 | 
        
           |  |  | 100 |         ];
 | 
        
           |  |  | 101 |     }
 | 
        
           |  |  | 102 |   | 
        
           |  |  | 103 |     /**
 | 
        
           |  |  | 104 |      * Returns a JSON object which corresponds to a condition of this type.
 | 
        
           |  |  | 105 |      *
 | 
        
           |  |  | 106 |      * Intended for unit testing, as normally the JSON values are constructed
 | 
        
           |  |  | 107 |      * by JavaScript code.
 | 
        
           |  |  | 108 |      *
 | 
        
           |  |  | 109 |      * @param int $cmid Course-module id of other activity
 | 
        
           |  |  | 110 |      * @param int $expectedcompletion Expected completion value (COMPLETION_xx)
 | 
        
           |  |  | 111 |      * @return stdClass Object representing condition
 | 
        
           |  |  | 112 |      */
 | 
        
           |  |  | 113 |     public static function get_json(int $cmid, int $expectedcompletion): stdClass {
 | 
        
           |  |  | 114 |         return (object) [
 | 
        
           |  |  | 115 |             'type' => 'completion',
 | 
        
           |  |  | 116 |             'cm' => (int)$cmid,
 | 
        
           |  |  | 117 |             'e' => (int)$expectedcompletion,
 | 
        
           |  |  | 118 |         ];
 | 
        
           |  |  | 119 |     }
 | 
        
           |  |  | 120 |   | 
        
           |  |  | 121 |     /**
 | 
        
           |  |  | 122 |      * Determines whether a particular item is currently available
 | 
        
           |  |  | 123 |      * according to this availability condition.
 | 
        
           |  |  | 124 |      *
 | 
        
           |  |  | 125 |      * @see \core_availability\tree_node\update_after_restore
 | 
        
           |  |  | 126 |      *
 | 
        
           |  |  | 127 |      * @param bool $not Set true if we are inverting the condition
 | 
        
           |  |  | 128 |      * @param info $info Item we're checking
 | 
        
           |  |  | 129 |      * @param bool $grabthelot Performance hint: if true, caches information
 | 
        
           |  |  | 130 |      *   required for all course-modules, to make the front page and similar
 | 
        
           |  |  | 131 |      *   pages work more quickly (works only for current user)
 | 
        
           |  |  | 132 |      * @param int $userid User ID to check availability for
 | 
        
           |  |  | 133 |      * @return bool True if available
 | 
        
           |  |  | 134 |      */
 | 
        
           |  |  | 135 |     public function is_available($not, info $info, $grabthelot, $userid): bool {
 | 
        
           |  |  | 136 |         list($selfcmid, $selfsectionid) = $this->get_selfids($info);
 | 
        
           |  |  | 137 |         $cmid = $this->get_cmid($info->get_course(), $selfcmid, $selfsectionid);
 | 
        
           |  |  | 138 |         $modinfo = $info->get_modinfo();
 | 
        
           |  |  | 139 |         $completion = new \completion_info($modinfo->get_course());
 | 
        
           |  |  | 140 |         if (!array_key_exists($cmid, $modinfo->cms) || $modinfo->cms[$cmid]->deletioninprogress) {
 | 
        
           |  |  | 141 |             // If the cmid cannot be found, always return false regardless
 | 
        
           |  |  | 142 |             // of the condition or $not state. (Will be displayed in the
 | 
        
           |  |  | 143 |             // information message.)
 | 
        
           |  |  | 144 |             $allow = false;
 | 
        
           |  |  | 145 |         } else {
 | 
        
           |  |  | 146 |             // The completion system caches its own data so no caching needed here.
 | 
        
           |  |  | 147 |             $completiondata = $completion->get_data((object)['id' => $cmid],
 | 
        
           |  |  | 148 |                     $grabthelot, $userid);
 | 
        
           |  |  | 149 |   | 
        
           |  |  | 150 |             $allow = true;
 | 
        
           |  |  | 151 |             if ($this->expectedcompletion == COMPLETION_COMPLETE) {
 | 
        
           |  |  | 152 |                 // Complete also allows the pass state.
 | 
        
           |  |  | 153 |                 switch ($completiondata->completionstate) {
 | 
        
           |  |  | 154 |                     case COMPLETION_COMPLETE:
 | 
        
           |  |  | 155 |                     case COMPLETION_COMPLETE_PASS:
 | 
        
           |  |  | 156 |                         break;
 | 
        
           |  |  | 157 |                     default:
 | 
        
           |  |  | 158 |                         $allow = false;
 | 
        
           |  |  | 159 |                 }
 | 
        
           |  |  | 160 |             } else if ($this->expectedcompletion == COMPLETION_INCOMPLETE) {
 | 
        
           |  |  | 161 |                 // Incomplete also allows the fail state.
 | 
        
           |  |  | 162 |                 switch ($completiondata->completionstate) {
 | 
        
           |  |  | 163 |                     case COMPLETION_INCOMPLETE:
 | 
        
           |  |  | 164 |                     case COMPLETION_COMPLETE_FAIL:
 | 
        
           |  |  | 165 |                         break;
 | 
        
           |  |  | 166 |                     default:
 | 
        
           |  |  | 167 |                         $allow = false;
 | 
        
           |  |  | 168 |                 }
 | 
        
           |  |  | 169 |             } else {
 | 
        
           |  |  | 170 |                 // Other values require exact match.
 | 
        
           |  |  | 171 |                 if ($completiondata->completionstate != $this->expectedcompletion) {
 | 
        
           |  |  | 172 |                     $allow = false;
 | 
        
           |  |  | 173 |                 }
 | 
        
           |  |  | 174 |             }
 | 
        
           |  |  | 175 |   | 
        
           |  |  | 176 |             if ($not) {
 | 
        
           |  |  | 177 |                 $allow = !$allow;
 | 
        
           |  |  | 178 |             }
 | 
        
           |  |  | 179 |         }
 | 
        
           |  |  | 180 |   | 
        
           |  |  | 181 |         return $allow;
 | 
        
           |  |  | 182 |     }
 | 
        
           |  |  | 183 |   | 
        
           |  |  | 184 |     /**
 | 
        
           |  |  | 185 |      * Return current item IDs (cmid and sectionid).
 | 
        
           |  |  | 186 |      *
 | 
        
           |  |  | 187 |      * @param info $info
 | 
        
           |  |  | 188 |      * @return int[] with [0] => cmid/null, [1] => sectionid/null
 | 
        
           |  |  | 189 |      */
 | 
        
           |  |  | 190 |     public function get_selfids(info $info): array {
 | 
        
           |  |  | 191 |         if (isset($this->selfids)) {
 | 
        
           |  |  | 192 |             return $this->selfids;
 | 
        
           |  |  | 193 |         }
 | 
        
           |  |  | 194 |         if ($info instanceof info_module) {
 | 
        
           |  |  | 195 |             $cminfo = $info->get_course_module();
 | 
        
           |  |  | 196 |             if (!empty($cminfo->id)) {
 | 
        
           |  |  | 197 |                 $this->selfids = [$cminfo->id, null];
 | 
        
           |  |  | 198 |                 return $this->selfids;
 | 
        
           |  |  | 199 |             }
 | 
        
           |  |  | 200 |         }
 | 
        
           |  |  | 201 |         if ($info instanceof info_section) {
 | 
        
           |  |  | 202 |             $section = $info->get_section();
 | 
        
           |  |  | 203 |             if (!empty($section->id)) {
 | 
        
           |  |  | 204 |                 $this->selfids = [null, $section->id];
 | 
        
           |  |  | 205 |                 return $this->selfids;
 | 
        
           |  |  | 206 |             }
 | 
        
           |  |  | 207 |   | 
        
           |  |  | 208 |         }
 | 
        
           |  |  | 209 |         return [null, null];
 | 
        
           |  |  | 210 |     }
 | 
        
           |  |  | 211 |   | 
        
           |  |  | 212 |     /**
 | 
        
           |  |  | 213 |      * Get the cmid referenced in the access restriction.
 | 
        
           |  |  | 214 |      *
 | 
        
           |  |  | 215 |      * @param stdClass $course course object
 | 
        
           |  |  | 216 |      * @param int|null $selfcmid current course-module ID or null
 | 
        
           |  |  | 217 |      * @param int|null $selfsectionid current course-section ID or null
 | 
        
           |  |  | 218 |      * @return int|null cmid or null if no referenced cm is found
 | 
        
           |  |  | 219 |      */
 | 
        
           |  |  | 220 |     public function get_cmid(stdClass $course, ?int $selfcmid, ?int $selfsectionid): ?int {
 | 
        
           |  |  | 221 |         if ($this->cmid > 0) {
 | 
        
           |  |  | 222 |             return $this->cmid;
 | 
        
           |  |  | 223 |         }
 | 
        
           |  |  | 224 |         // If it's a relative completion, load fast browsing.
 | 
        
           |  |  | 225 |         if ($this->cmid == self::OPTION_PREVIOUS) {
 | 
        
           |  |  | 226 |             $prevcmid = $this->get_previous_cmid($course, $selfcmid, $selfsectionid);
 | 
        
           |  |  | 227 |             if ($prevcmid) {
 | 
        
           |  |  | 228 |                 return $prevcmid;
 | 
        
           |  |  | 229 |             }
 | 
        
           |  |  | 230 |         }
 | 
        
           |  |  | 231 |         return null;
 | 
        
           |  |  | 232 |     }
 | 
        
           |  |  | 233 |   | 
        
           |  |  | 234 |     /**
 | 
        
           |  |  | 235 |      * Return the previous CM ID of an specific course-module or course-section.
 | 
        
           |  |  | 236 |      *
 | 
        
           |  |  | 237 |      * @param stdClass $course course object
 | 
        
           |  |  | 238 |      * @param int|null $selfcmid course-module ID or null
 | 
        
           |  |  | 239 |      * @param int|null $selfsectionid course-section ID or null
 | 
        
           |  |  | 240 |      * @return int|null
 | 
        
           |  |  | 241 |      */
 | 
        
           |  |  | 242 |     private function get_previous_cmid(stdClass $course, ?int $selfcmid, ?int $selfsectionid): ?int {
 | 
        
           |  |  | 243 |         $this->load_course_structure($course);
 | 
        
           |  |  | 244 |         if (isset($this->modfastprevious[$selfcmid])) {
 | 
        
           |  |  | 245 |             return $this->modfastprevious[$selfcmid];
 | 
        
           |  |  | 246 |         }
 | 
        
           |  |  | 247 |         if (isset($this->sectionfastprevious[$selfsectionid])) {
 | 
        
           |  |  | 248 |             return $this->sectionfastprevious[$selfsectionid];
 | 
        
           |  |  | 249 |         }
 | 
        
           |  |  | 250 |         return null;
 | 
        
           |  |  | 251 |     }
 | 
        
           |  |  | 252 |   | 
        
           |  |  | 253 |     /**
 | 
        
           |  |  | 254 |      * Loads static information about a course elements previous activities.
 | 
        
           |  |  | 255 |      *
 | 
        
           |  |  | 256 |      * Populates two variables:
 | 
        
           |  |  | 257 |      *   - $this->sectionprevious[] course-module previous to a cmid
 | 
        
           |  |  | 258 |      *   - $this->sectionfastprevious[] course-section previous to a cmid
 | 
        
           |  |  | 259 |      *
 | 
        
           |  |  | 260 |      * @param stdClass $course course object
 | 
        
           |  |  | 261 |      */
 | 
        
           |  |  | 262 |     private function load_course_structure(stdClass $course): void {
 | 
        
           |  |  | 263 |         // If already loaded we don't need to do anything.
 | 
        
           |  |  | 264 |         if (empty($this->modfastprevious)) {
 | 
        
           |  |  | 265 |             $previouscache = cache::make('availability_completion', 'previous_cache');
 | 
        
           |  |  | 266 |             $this->modfastprevious = $previouscache->get("mod_{$course->id}");
 | 
        
           |  |  | 267 |             $this->sectionfastprevious = $previouscache->get("sec_{$course->id}");
 | 
        
           |  |  | 268 |         }
 | 
        
           |  |  | 269 |   | 
        
           |  |  | 270 |         if (!empty($this->modfastprevious)) {
 | 
        
           |  |  | 271 |             return;
 | 
        
           |  |  | 272 |         }
 | 
        
           |  |  | 273 |   | 
        
           |  |  | 274 |         if (empty($this->modfastprevious)) {
 | 
        
           |  |  | 275 |             $this->modfastprevious = [];
 | 
        
           |  |  | 276 |             $sectionprevious = [];
 | 
        
           |  |  | 277 |   | 
        
           |  |  | 278 |             $modinfo = get_fast_modinfo($course);
 | 
        
           |  |  | 279 |             $lastcmid = 0;
 | 
        
           |  |  | 280 |             foreach ($modinfo->cms as $othercm) {
 | 
        
           |  |  | 281 |                 if ($othercm->deletioninprogress) {
 | 
        
           |  |  | 282 |                     continue;
 | 
        
           |  |  | 283 |                 }
 | 
        
           |  |  | 284 |                 // Save first cm of every section.
 | 
        
           |  |  | 285 |                 if (!isset($sectionprevious[$othercm->section])) {
 | 
        
           |  |  | 286 |                     $sectionprevious[$othercm->section] = $lastcmid;
 | 
        
           |  |  | 287 |                 }
 | 
        
           |  |  | 288 |                 if ($lastcmid) {
 | 
        
           |  |  | 289 |                     $this->modfastprevious[$othercm->id] = $lastcmid;
 | 
        
           |  |  | 290 |                 }
 | 
        
           |  |  | 291 |                 // Load previous to all cms with completion.
 | 
        
           |  |  | 292 |                 if ($othercm->completion == COMPLETION_TRACKING_NONE) {
 | 
        
           |  |  | 293 |                     continue;
 | 
        
           |  |  | 294 |                 }
 | 
        
           |  |  | 295 |                 $lastcmid = $othercm->id;
 | 
        
           |  |  | 296 |             }
 | 
        
           |  |  | 297 |             // Fill empty sections index.
 | 
        
           |  |  | 298 |             $isections = array_reverse($modinfo->get_section_info_all());
 | 
        
           |  |  | 299 |             foreach ($isections as $section) {
 | 
        
           |  |  | 300 |                 if (isset($sectionprevious[$section->id])) {
 | 
        
           |  |  | 301 |                     $lastcmid = $sectionprevious[$section->id];
 | 
        
           |  |  | 302 |                 } else {
 | 
        
           |  |  | 303 |                     $sectionprevious[$section->id] = $lastcmid;
 | 
        
           |  |  | 304 |                 }
 | 
        
           |  |  | 305 |             }
 | 
        
           |  |  | 306 |             $this->sectionfastprevious = $sectionprevious;
 | 
        
           |  |  | 307 |             $previouscache->set("mod_{$course->id}", $this->modfastprevious);
 | 
        
           |  |  | 308 |             $previouscache->set("sec_{$course->id}", $this->sectionfastprevious);
 | 
        
           |  |  | 309 |         }
 | 
        
           |  |  | 310 |     }
 | 
        
           |  |  | 311 |   | 
        
           |  |  | 312 |     /**
 | 
        
           |  |  | 313 |      * Returns a more readable keyword corresponding to a completion state.
 | 
        
           |  |  | 314 |      *
 | 
        
           |  |  | 315 |      * Used to make lang strings easier to read.
 | 
        
           |  |  | 316 |      *
 | 
        
           |  |  | 317 |      * @param int $completionstate COMPLETION_xx constant
 | 
        
           |  |  | 318 |      * @return string Readable keyword
 | 
        
           |  |  | 319 |      */
 | 
        
           |  |  | 320 |     protected static function get_lang_string_keyword(int $completionstate): string {
 | 
        
           |  |  | 321 |         switch($completionstate) {
 | 
        
           |  |  | 322 |             case COMPLETION_INCOMPLETE:
 | 
        
           |  |  | 323 |                 return 'incomplete';
 | 
        
           |  |  | 324 |             case COMPLETION_COMPLETE:
 | 
        
           |  |  | 325 |                 return 'complete';
 | 
        
           |  |  | 326 |             case COMPLETION_COMPLETE_PASS:
 | 
        
           |  |  | 327 |                 return 'complete_pass';
 | 
        
           |  |  | 328 |             case COMPLETION_COMPLETE_FAIL:
 | 
        
           |  |  | 329 |                 return 'complete_fail';
 | 
        
           |  |  | 330 |             default:
 | 
        
           |  |  | 331 |                 throw new \coding_exception('Unexpected completion state: ' . $completionstate);
 | 
        
           |  |  | 332 |         }
 | 
        
           |  |  | 333 |     }
 | 
        
           |  |  | 334 |   | 
        
           |  |  | 335 |     /**
 | 
        
           |  |  | 336 |      * Obtains a string describing this restriction (whether or not
 | 
        
           |  |  | 337 |      * it actually applies).
 | 
        
           |  |  | 338 |      *
 | 
        
           |  |  | 339 |      * @param bool $full Set true if this is the 'full information' view
 | 
        
           |  |  | 340 |      * @param bool $not Set true if we are inverting the condition
 | 
        
           |  |  | 341 |      * @param info $info Item we're checking
 | 
        
           |  |  | 342 |      * @return string Information string (for admin) about all restrictions on
 | 
        
           |  |  | 343 |      *   this item
 | 
        
           |  |  | 344 |      */
 | 
        
           |  |  | 345 |     public function get_description($full, $not, info $info): string {
 | 
        
           |  |  | 346 |         global $USER;
 | 
        
           |  |  | 347 |         $str = 'requires_';
 | 
        
           |  |  | 348 |         $course = $info->get_course();
 | 
        
           |  |  | 349 |         list($selfcmid, $selfsectionid) = $this->get_selfids($info);
 | 
        
           |  |  | 350 |         $modname = '';
 | 
        
           |  |  | 351 |         // On ajax duplicate get_fast_modinfo is called before $PAGE->set_context
 | 
        
           |  |  | 352 |         // so we cannot use $PAGE->user_is_editing().
 | 
        
           |  |  | 353 |         $coursecontext = \context_course::instance($course->id);
 | 
        
           |  |  | 354 |         $editing = !empty($USER->editing) && has_capability('moodle/course:manageactivities', $coursecontext);
 | 
        
           |  |  | 355 |         if ($this->cmid == self::OPTION_PREVIOUS && $editing) {
 | 
        
           |  |  | 356 |             // Previous activity name could be inconsistent when editing due to partial page loadings.
 | 
        
           |  |  | 357 |             $str .= 'previous_';
 | 
        
           |  |  | 358 |         } else {
 | 
        
           |  |  | 359 |             // Get name for module.
 | 
        
           |  |  | 360 |             $cmid = $this->get_cmid($course, $selfcmid, $selfsectionid);
 | 
        
           |  |  | 361 |             $modinfo = $info->get_modinfo();
 | 
        
           |  |  | 362 |             if (!array_key_exists($cmid, $modinfo->cms) || $modinfo->cms[$cmid]->deletioninprogress) {
 | 
        
           |  |  | 363 |                 $modname = get_string('missing', 'availability_completion');
 | 
        
           |  |  | 364 |             } else {
 | 
        
           |  |  | 365 |                 $modname = self::description_cm_name($modinfo->cms[$cmid]->id);
 | 
        
           |  |  | 366 |             }
 | 
        
           |  |  | 367 |         }
 | 
        
           |  |  | 368 |   | 
        
           |  |  | 369 |         // Work out which lang string to use depending on required completion status.
 | 
        
           |  |  | 370 |         if ($not) {
 | 
        
           |  |  | 371 |             // Convert NOT strings to use the equivalent where possible.
 | 
        
           |  |  | 372 |             switch ($this->expectedcompletion) {
 | 
        
           |  |  | 373 |                 case COMPLETION_INCOMPLETE:
 | 
        
           |  |  | 374 |                     $str .= self::get_lang_string_keyword(COMPLETION_COMPLETE);
 | 
        
           |  |  | 375 |                     break;
 | 
        
           |  |  | 376 |                 case COMPLETION_COMPLETE:
 | 
        
           |  |  | 377 |                     $str .= self::get_lang_string_keyword(COMPLETION_INCOMPLETE);
 | 
        
           |  |  | 378 |                     break;
 | 
        
           |  |  | 379 |                 default:
 | 
        
           |  |  | 380 |                     // The other two cases do not have direct opposites.
 | 
        
           |  |  | 381 |                     $str .= 'not_' . self::get_lang_string_keyword($this->expectedcompletion);
 | 
        
           |  |  | 382 |                     break;
 | 
        
           |  |  | 383 |             }
 | 
        
           |  |  | 384 |         } else {
 | 
        
           |  |  | 385 |             $str .= self::get_lang_string_keyword($this->expectedcompletion);
 | 
        
           |  |  | 386 |         }
 | 
        
           |  |  | 387 |   | 
        
           |  |  | 388 |         return get_string($str, 'availability_completion', $modname);
 | 
        
           |  |  | 389 |     }
 | 
        
           |  |  | 390 |   | 
        
           |  |  | 391 |     /**
 | 
        
           |  |  | 392 |      * Obtains a representation of the options of this condition as a string,
 | 
        
           |  |  | 393 |      * for debugging.
 | 
        
           |  |  | 394 |      *
 | 
        
           |  |  | 395 |      * @return string Text representation of parameters
 | 
        
           |  |  | 396 |      */
 | 
        
           |  |  | 397 |     protected function get_debug_string(): string {
 | 
        
           |  |  | 398 |         switch ($this->expectedcompletion) {
 | 
        
           |  |  | 399 |             case COMPLETION_COMPLETE :
 | 
        
           |  |  | 400 |                 $type = 'COMPLETE';
 | 
        
           |  |  | 401 |                 break;
 | 
        
           |  |  | 402 |             case COMPLETION_INCOMPLETE :
 | 
        
           |  |  | 403 |                 $type = 'INCOMPLETE';
 | 
        
           |  |  | 404 |                 break;
 | 
        
           |  |  | 405 |             case COMPLETION_COMPLETE_PASS:
 | 
        
           |  |  | 406 |                 $type = 'COMPLETE_PASS';
 | 
        
           |  |  | 407 |                 break;
 | 
        
           |  |  | 408 |             case COMPLETION_COMPLETE_FAIL:
 | 
        
           |  |  | 409 |                 $type = 'COMPLETE_FAIL';
 | 
        
           |  |  | 410 |                 break;
 | 
        
           |  |  | 411 |             default:
 | 
        
           |  |  | 412 |                 throw new \coding_exception('Unexpected expected completion');
 | 
        
           |  |  | 413 |         }
 | 
        
           |  |  | 414 |         $cm = $this->cmid;
 | 
        
           |  |  | 415 |         if ($this->cmid == self::OPTION_PREVIOUS) {
 | 
        
           |  |  | 416 |             $cm = 'opprevious';
 | 
        
           |  |  | 417 |         }
 | 
        
           |  |  | 418 |         return 'cm' . $cm . ' ' . $type;
 | 
        
           |  |  | 419 |     }
 | 
        
           |  |  | 420 |   | 
        
           |  |  | 421 |     /**
 | 
        
           |  |  | 422 |      * Updates this node after restore, returning true if anything changed.
 | 
        
           |  |  | 423 |      *
 | 
        
           |  |  | 424 |      * @see \core_availability\tree_node\update_after_restore
 | 
        
           |  |  | 425 |      *
 | 
        
           |  |  | 426 |      * @param string $restoreid Restore ID
 | 
        
           |  |  | 427 |      * @param int $courseid ID of target course
 | 
        
           |  |  | 428 |      * @param \base_logger $logger Logger for any warnings
 | 
        
           |  |  | 429 |      * @param string $name Name of this item (for use in warning messages)
 | 
        
           |  |  | 430 |      * @return bool True if there was any change
 | 
        
           |  |  | 431 |      */
 | 
        
           |  |  | 432 |     public function update_after_restore($restoreid, $courseid, \base_logger $logger, $name): bool {
 | 
        
           |  |  | 433 |         global $DB;
 | 
        
           |  |  | 434 |         $res = false;
 | 
        
           |  |  | 435 |         // If we depend on the previous activity, no translation is needed.
 | 
        
           |  |  | 436 |         if ($this->cmid == self::OPTION_PREVIOUS) {
 | 
        
           |  |  | 437 |             return $res;
 | 
        
           |  |  | 438 |         }
 | 
        
           |  |  | 439 |         $rec = \restore_dbops::get_backup_ids_record($restoreid, 'course_module', $this->cmid);
 | 
        
           |  |  | 440 |         if (!$rec || !$rec->newitemid) {
 | 
        
           |  |  | 441 |             // If we are on the same course (e.g. duplicate) then we can just
 | 
        
           |  |  | 442 |             // use the existing one.
 | 
        
           |  |  | 443 |             if ($DB->record_exists('course_modules',
 | 
        
           |  |  | 444 |                     ['id' => $this->cmid, 'course' => $courseid])) {
 | 
        
           |  |  | 445 |                 return $res;
 | 
        
           |  |  | 446 |             }
 | 
        
           |  |  | 447 |             // Otherwise it's a warning.
 | 
        
           |  |  | 448 |             $this->cmid = 0;
 | 
        
           |  |  | 449 |             $logger->process('Restored item (' . $name .
 | 
        
           |  |  | 450 |                     ') has availability condition on module that was not restored',
 | 
        
           |  |  | 451 |                     \backup::LOG_WARNING);
 | 
        
           |  |  | 452 |         } else {
 | 
        
           |  |  | 453 |             $this->cmid = (int)$rec->newitemid;
 | 
        
           |  |  | 454 |         }
 | 
        
           |  |  | 455 |         return true;
 | 
        
           |  |  | 456 |     }
 | 
        
           |  |  | 457 |   | 
        
           |  |  | 458 |     /**
 | 
        
           |  |  | 459 |      * Used in course/lib.php because we need to disable the completion JS if
 | 
        
           |  |  | 460 |      * a completion value affects a conditional activity.
 | 
        
           |  |  | 461 |      *
 | 
        
           |  |  | 462 |      * @param \stdClass $course Moodle course object
 | 
        
           |  |  | 463 |      * @param int $cmid Course-module id
 | 
        
           |  |  | 464 |      * @return bool True if this is used in a condition, false otherwise
 | 
        
           |  |  | 465 |      */
 | 
        
           |  |  | 466 |     public static function completion_value_used($course, $cmid): bool {
 | 
        
           |  |  | 467 |         // Have we already worked out a list of required completion values
 | 
        
           |  |  | 468 |         // for this course? If so just use that.
 | 
        
           |  |  | 469 |         if (!array_key_exists($course->id, self::$modsusedincondition)) {
 | 
        
           |  |  | 470 |             // We don't have data for this course, build it.
 | 
        
           |  |  | 471 |             $modinfo = get_fast_modinfo($course);
 | 
        
           |  |  | 472 |             self::$modsusedincondition[$course->id] = [];
 | 
        
           |  |  | 473 |   | 
        
           |  |  | 474 |             // Activities.
 | 
        
           |  |  | 475 |             foreach ($modinfo->cms as $othercm) {
 | 
        
           |  |  | 476 |                 if (is_null($othercm->availability)) {
 | 
        
           |  |  | 477 |                     continue;
 | 
        
           |  |  | 478 |                 }
 | 
        
           |  |  | 479 |                 $ci = new \core_availability\info_module($othercm);
 | 
        
           |  |  | 480 |                 $tree = $ci->get_availability_tree();
 | 
        
           |  |  | 481 |                 foreach ($tree->get_all_children('availability_completion\condition') as $cond) {
 | 
        
           |  |  | 482 |                     $condcmid = $cond->get_cmid($course, $othercm->id, null);
 | 
        
           |  |  | 483 |                     if (!empty($condcmid)) {
 | 
        
           |  |  | 484 |                         self::$modsusedincondition[$course->id][$condcmid] = true;
 | 
        
           |  |  | 485 |                     }
 | 
        
           |  |  | 486 |                 }
 | 
        
           |  |  | 487 |             }
 | 
        
           |  |  | 488 |   | 
        
           |  |  | 489 |             // Sections.
 | 
        
           |  |  | 490 |             foreach ($modinfo->get_section_info_all() as $section) {
 | 
        
           |  |  | 491 |                 if (is_null($section->availability)) {
 | 
        
           |  |  | 492 |                     continue;
 | 
        
           |  |  | 493 |                 }
 | 
        
           |  |  | 494 |                 $ci = new \core_availability\info_section($section);
 | 
        
           |  |  | 495 |                 $tree = $ci->get_availability_tree();
 | 
        
           |  |  | 496 |                 foreach ($tree->get_all_children('availability_completion\condition') as $cond) {
 | 
        
           |  |  | 497 |                     $condcmid = $cond->get_cmid($course, null, $section->id);
 | 
        
           |  |  | 498 |                     if (!empty($condcmid)) {
 | 
        
           |  |  | 499 |                         self::$modsusedincondition[$course->id][$condcmid] = true;
 | 
        
           |  |  | 500 |                     }
 | 
        
           |  |  | 501 |                 }
 | 
        
           |  |  | 502 |             }
 | 
        
           |  |  | 503 |         }
 | 
        
           |  |  | 504 |         return array_key_exists($cmid, self::$modsusedincondition[$course->id]);
 | 
        
           |  |  | 505 |     }
 | 
        
           |  |  | 506 |   | 
        
           |  |  | 507 |     /**
 | 
        
           |  |  | 508 |      * Wipes the static cache of modules used in a condition (for unit testing).
 | 
        
           |  |  | 509 |      */
 | 
        
           |  |  | 510 |     public static function wipe_static_cache() {
 | 
        
           |  |  | 511 |         self::$modsusedincondition = [];
 | 
        
           |  |  | 512 |     }
 | 
        
           |  |  | 513 |   | 
        
           |  |  | 514 |     public function update_dependency_id($table, $oldid, $newid) {
 | 
        
           |  |  | 515 |         if ($table === 'course_modules' && (int)$this->cmid === (int)$oldid) {
 | 
        
           |  |  | 516 |             $this->cmid = $newid;
 | 
        
           |  |  | 517 |             return true;
 | 
        
           |  |  | 518 |         } else {
 | 
        
           |  |  | 519 |             return false;
 | 
        
           |  |  | 520 |         }
 | 
        
           |  |  | 521 |     }
 | 
        
           |  |  | 522 | }
 |