weekly release 3.9dev
[moodle.git] / availability / condition / completion / classes / condition.php
CommitLineData
e01efa2c 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
25namespace availability_completion;
26
27defined('MOODLE_INTERNAL') || die();
28
29require_once($CFG->libdir . '/completionlib.php');
30
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 */
38class condition extends \core_availability\condition {
39 /** @var int ID of module that this depends on */
40 protected $cmid;
41
42 /** @var int Expected completion type (one of the COMPLETE_xx constants) */
43 protected $expectedcompletion;
44
45 /** @var array Array of modules used in these conditions for course */
46 protected static $modsusedincondition = array();
47
48 /**
49 * Constructor.
50 *
5cad8d35
AA
51 * @param \stdClass $structure Data structure from JSON decode
52 * @throws \coding_exception If invalid data structure.
e01efa2c 53 */
54 public function __construct($structure) {
55 // Get cmid.
729a6c56
DM
56 if (isset($structure->cm) && is_number($structure->cm)) {
57 $this->cmid = (int)$structure->cm;
e01efa2c 58 } else {
59 throw new \coding_exception('Missing or invalid ->cm for completion condition');
60 }
61
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 }
71
72 public function save() {
73 return (object)array('type' => 'completion',
74 'cm' => $this->cmid, 'e' => $this->expectedcompletion);
75 }
76
06c06038 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 }
91
e01efa2c 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);
104
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 }
122
123 if ($not) {
124 $allow = !$allow;
125 }
126 }
127
128 return $allow;
129 }
130
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 }
153
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 }
162
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 }
181
182 return get_string($str, 'availability_completion', $modname);
183 }
184
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 }
204
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 }
225
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 *
5cad8d35 230 * @param \stdClass $course Moodle course object
e01efa2c 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();
241
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 }
253
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 }
268
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 }
275
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 }
284}