MDL-68430 filter_mathjaxloader: update default CDN to 2.7.8
[moodle.git] / availability / condition / completion / classes / condition.php
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/>.
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  */
25 namespace availability_completion;
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->libdir . '/completionlib.php');
31 /**
32  * Activity completion condition.
33  *
34  * @package availability_completion
35  * @copyright 2014 The Open University
36  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class condition extends \core_availability\condition {
39     /** @var int ID of module that this depends on */
40     protected $cmid;
42     /** @var int Expected completion type (one of the COMPLETE_xx constants) */
43     protected $expectedcompletion;
45     /** @var array Array of modules used in these conditions for course */
46     protected static $modsusedincondition = array();
48     /**
49      * Constructor.
50      *
51      * @param \stdClass $structure Data structure from JSON decode
52      * @throws \coding_exception If invalid data structure.
53      */
54     public function __construct($structure) {
55         // Get cmid.
56         if (isset($structure->cm) && is_number($structure->cm)) {
57             $this->cmid = (int)$structure->cm;
58         } else {
59             throw new \coding_exception('Missing or invalid ->cm for completion condition');
60         }
62         // Get expected completion.
63         if (isset($structure->e) && in_array($structure->e,
64                 array(COMPLETION_COMPLETE, COMPLETION_INCOMPLETE,
65                         COMPLETION_COMPLETE_PASS, COMPLETION_COMPLETE_FAIL))) {
66             $this->expectedcompletion = $structure->e;
67         } else {
68             throw new \coding_exception('Missing or invalid ->e for completion condition');
69         }
70     }
72     public function save() {
73         return (object)array('type' => 'completion',
74                 'cm' => $this->cmid, 'e' => $this->expectedcompletion);
75     }
77     /**
78      * Returns a JSON object which corresponds to a condition of this type.
79      *
80      * Intended for unit testing, as normally the JSON values are constructed
81      * by JavaScript code.
82      *
83      * @param int $cmid Course-module id of other activity
84      * @param int $expectedcompletion Expected completion value (COMPLETION_xx)
85      * @return stdClass Object representing condition
86      */
87     public static function get_json($cmid, $expectedcompletion) {
88         return (object)array('type' => 'completion', 'cm' => (int)$cmid,
89                 'e' => (int)$expectedcompletion);
90     }
92     public function is_available($not, \core_availability\info $info, $grabthelot, $userid) {
93         $modinfo = $info->get_modinfo();
94         $completion = new \completion_info($modinfo->get_course());
95         if (!array_key_exists($this->cmid, $modinfo->cms)) {
96             // If the cmid cannot be found, always return false regardless
97             // of the condition or $not state. (Will be displayed in the
98             // information message.)
99             $allow = false;
100         } else {
101             // The completion system caches its own data so no caching needed here.
102             $completiondata = $completion->get_data((object)array('id' => $this->cmid),
103                     $grabthelot, $userid, $modinfo);
105             $allow = true;
106             if ($this->expectedcompletion == COMPLETION_COMPLETE) {
107                 // Complete also allows the pass, fail states.
108                 switch ($completiondata->completionstate) {
109                     case COMPLETION_COMPLETE:
110                     case COMPLETION_COMPLETE_FAIL:
111                     case COMPLETION_COMPLETE_PASS:
112                         break;
113                     default:
114                         $allow = false;
115                 }
116             } else {
117                 // Other values require exact match.
118                 if ($completiondata->completionstate != $this->expectedcompletion) {
119                     $allow = false;
120                 }
121             }
123             if ($not) {
124                 $allow = !$allow;
125             }
126         }
128         return $allow;
129     }
131     /**
132      * Returns a more readable keyword corresponding to a completion state.
133      *
134      * Used to make lang strings easier to read.
135      *
136      * @param int $completionstate COMPLETION_xx constant
137      * @return string Readable keyword
138      */
139     protected static function get_lang_string_keyword($completionstate) {
140         switch($completionstate) {
141             case COMPLETION_INCOMPLETE:
142                 return 'incomplete';
143             case COMPLETION_COMPLETE:
144                 return 'complete';
145             case COMPLETION_COMPLETE_PASS:
146                 return 'complete_pass';
147             case COMPLETION_COMPLETE_FAIL:
148                 return 'complete_fail';
149             default:
150                 throw new \coding_exception('Unexpected completion state: ' . $completionstate);
151         }
152     }
154     public function get_description($full, $not, \core_availability\info $info) {
155         // Get name for module.
156         $modinfo = $info->get_modinfo();
157         if (!array_key_exists($this->cmid, $modinfo->cms)) {
158             $modname = get_string('missing', 'availability_completion');
159         } else {
160             $modname = '<AVAILABILITY_CMNAME_' . $modinfo->cms[$this->cmid]->id . '/>';
161         }
163         // Work out which lang string to use.
164         if ($not) {
165             // Convert NOT strings to use the equivalent where possible.
166             switch ($this->expectedcompletion) {
167                 case COMPLETION_INCOMPLETE:
168                     $str = 'requires_' . self::get_lang_string_keyword(COMPLETION_COMPLETE);
169                     break;
170                 case COMPLETION_COMPLETE:
171                     $str = 'requires_' . self::get_lang_string_keyword(COMPLETION_INCOMPLETE);
172                     break;
173                 default:
174                     // The other two cases do not have direct opposites.
175                     $str = 'requires_not_' . self::get_lang_string_keyword($this->expectedcompletion);
176                     break;
177             }
178         } else {
179             $str = 'requires_' . self::get_lang_string_keyword($this->expectedcompletion);
180         }
182         return get_string($str, 'availability_completion', $modname);
183     }
185     protected function get_debug_string() {
186         switch ($this->expectedcompletion) {
187             case COMPLETION_COMPLETE :
188                 $type = 'COMPLETE';
189                 break;
190             case COMPLETION_INCOMPLETE :
191                 $type = 'INCOMPLETE';
192                 break;
193             case COMPLETION_COMPLETE_PASS:
194                 $type = 'COMPLETE_PASS';
195                 break;
196             case COMPLETION_COMPLETE_FAIL:
197                 $type = 'COMPLETE_FAIL';
198                 break;
199             default:
200                 throw new \coding_exception('Unexpected expected completion');
201         }
202         return 'cm' . $this->cmid . ' ' . $type;
203     }
205     public function update_after_restore($restoreid, $courseid, \base_logger $logger, $name) {
206         global $DB;
207         $rec = \restore_dbops::get_backup_ids_record($restoreid, 'course_module', $this->cmid);
208         if (!$rec || !$rec->newitemid) {
209             // If we are on the same course (e.g. duplicate) then we can just
210             // use the existing one.
211             if ($DB->record_exists('course_modules',
212                     array('id' => $this->cmid, 'course' => $courseid))) {
213                 return false;
214             }
215             // Otherwise it's a warning.
216             $this->cmid = 0;
217             $logger->process('Restored item (' . $name .
218                     ') has availability condition on module that was not restored',
219                     \backup::LOG_WARNING);
220         } else {
221             $this->cmid = (int)$rec->newitemid;
222         }
223         return true;
224     }
226     /**
227      * Used in course/lib.php because we need to disable the completion JS if
228      * a completion value affects a conditional activity.
229      *
230      * @param \stdClass $course Moodle course object
231      * @param int $cmid Course-module id
232      * @return bool True if this is used in a condition, false otherwise
233      */
234     public static function completion_value_used($course, $cmid) {
235         // Have we already worked out a list of required completion values
236         // for this course? If so just use that.
237         if (!array_key_exists($course->id, self::$modsusedincondition)) {
238             // We don't have data for this course, build it.
239             $modinfo = get_fast_modinfo($course);
240             self::$modsusedincondition[$course->id] = array();
242             // Activities.
243             foreach ($modinfo->cms as $othercm) {
244                 if (is_null($othercm->availability)) {
245                     continue;
246                 }
247                 $ci = new \core_availability\info_module($othercm);
248                 $tree = $ci->get_availability_tree();
249                 foreach ($tree->get_all_children('availability_completion\condition') as $cond) {
250                     self::$modsusedincondition[$course->id][$cond->cmid] = true;
251                 }
252             }
254             // Sections.
255             foreach ($modinfo->get_section_info_all() as $section) {
256                 if (is_null($section->availability)) {
257                     continue;
258                 }
259                 $ci = new \core_availability\info_section($section);
260                 $tree = $ci->get_availability_tree();
261                 foreach ($tree->get_all_children('availability_completion\condition') as $cond) {
262                     self::$modsusedincondition[$course->id][$cond->cmid] = true;
263                 }
264             }
265         }
266         return array_key_exists($cmid, self::$modsusedincondition[$course->id]);
267     }
269     /**
270      * Wipes the static cache of modules used in a condition (for unit testing).
271      */
272     public static function wipe_static_cache() {
273         self::$modsusedincondition = array();
274     }
276     public function update_dependency_id($table, $oldid, $newid) {
277         if ($table === 'course_modules' && (int)$this->cmid === (int)$oldid) {
278             $this->cmid = $newid;
279             return true;
280         } else {
281             return false;
282         }
283     }