Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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
 * Contains the class for building the user's activity completion details.
19
 *
20
 * @package   core_completion
21
 * @copyright 2021 Jun Pataleta <jun@moodle.com>
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
declare(strict_types = 1);
26
 
27
namespace core_completion;
28
 
29
use cm_info;
30
use completion_info;
31
 
32
/**
33
 * Class for building the user's activity completion details.
34
 *
35
 * @package   core_completion
36
 * @copyright 2021 Jun Pataleta <jun@moodle.com>
37
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
class cm_completion_details {
40
    /** @var completion_info The completion info instance for this cm's course. */
41
    protected $completioninfo = null;
42
 
43
    /** @var object The completion data. */
44
    protected $completiondata = null;
45
 
46
    /** @var cm_info The course module information. */
47
    protected $cminfo = null;
48
 
49
    /** @var int The user ID. */
50
    protected $userid = 0;
51
 
52
    /** @var bool Whether to return automatic completion details. */
53
    protected $returndetails = true;
54
 
55
    /** @var activity_custom_completion Activity custom completion object. */
56
    protected $cmcompletion = null;
57
 
58
    /**
59
     * Constructor.
60
     *
61
     * @param completion_info $completioninfo The completion info instance for this cm's course.
62
     * @param cm_info $cminfo The course module information.
63
     * @param int $userid The user ID.
64
     * @param bool $returndetails Whether to return completion details or not.
65
     */
66
    public function __construct(completion_info $completioninfo, cm_info $cminfo, int $userid, bool $returndetails = true) {
67
        $this->completioninfo = $completioninfo;
68
        // We need to pass wholecourse = true here for better performance. All the course's completion data for the current
69
        // logged-in user will get in a single query instead of multiple queries and loaded to cache.
70
        $this->completiondata = $completioninfo->get_data($cminfo, true, $userid);
71
        $this->cminfo = $cminfo;
72
        $this->userid = $userid;
73
        $this->returndetails = $returndetails;
74
        $cmcompletionclass = activity_custom_completion::get_cm_completion_class($this->cminfo->modname);
75
        if ($cmcompletionclass) {
76
            $this->cmcompletion = new $cmcompletionclass(
77
                $this->cminfo,
78
                $this->userid,
79
                $completioninfo->get_core_completion_state($cminfo, $userid)
80
            );
81
        }
82
    }
83
 
84
    /**
85
     * Fetches the completion details for a user.
86
     *
87
     * @return array An array of completion details for a user containing the completion requirement's description and status.
88
     * @throws \coding_exception
89
     */
90
    public function get_details(): array {
91
        if (!$this->is_automatic()) {
92
            // No details need to be returned for modules that don't have automatic completion tracking enabled.
93
            return [];
94
        }
95
 
96
        if (!$this->returndetails) {
97
            // We don't need to return completion details.
98
            return [];
99
        }
100
 
101
        $completiondata = $this->completiondata;
102
        $hasoverride = !empty($this->overridden_by());
103
 
104
        $details = [];
105
 
106
        // Completion rule: Student must view this activity.
107
        if (!empty($this->cminfo->completionview)) {
108
            if (!$hasoverride) {
109
                $status = COMPLETION_INCOMPLETE;
110
                if ($completiondata->viewed == COMPLETION_VIEWED) {
111
                    $status = COMPLETION_COMPLETE;
112
                }
113
            } else {
114
                $status = $completiondata->completionstate;
115
            }
116
 
117
            $details['completionview'] = (object)[
118
                'status' => $status,
119
                'description' => get_string('detail_desc:view', 'completion'),
120
            ];
121
        }
122
 
123
        // Completion rule: Student must receive a grade.
124
        if (!is_null($this->cminfo->completiongradeitemnumber)) {
125
            if (!$hasoverride) {
126
                $status = $completiondata->completiongrade ?? COMPLETION_INCOMPLETE;
127
            } else {
128
                $status = $completiondata->completionstate;
129
            }
130
 
131
            $details['completionusegrade'] = (object)[
132
                'status' => $status,
133
                'description' => get_string('detail_desc:receivegrade', 'completion'),
134
            ];
135
 
136
            if (!is_null($this->cminfo->completionpassgrade) && $this->cminfo->completionpassgrade) {
137
                $details['completionpassgrade'] = (object)[
138
                    'status' => $completiondata->passgrade ?? COMPLETION_INCOMPLETE,
139
                    'description' => get_string('detail_desc:receivepassgrade', 'completion'),
140
                ];
141
            }
142
        }
143
 
144
        if ($this->cmcompletion) {
145
            if (isset($completiondata->customcompletion)) {
146
                foreach ($completiondata->customcompletion as $rule => $status) {
147
                    $details[$rule] = (object)[
148
                        'status' => !$hasoverride ? $status : $completiondata->completionstate,
149
                        'description' => $this->cmcompletion->get_custom_rule_description($rule),
150
                    ];
151
                }
152
 
153
                $details = $this->sort_completion_details($details);
154
            }
155
        }
156
 
157
        return $details;
158
    }
159
 
160
    /**
161
     * Sort completion details in the order specified by the activity's custom completion implementation.
162
     *
163
     * @param array $details The completion details to be sorted.
164
     * @return array
165
     * @throws \coding_exception
166
     */
167
    protected function sort_completion_details(array $details): array {
168
        $sortorder = $this->cmcompletion->get_sort_order();
169
        $sorteddetails = [];
170
 
171
        foreach ($sortorder as $sortedkey) {
172
            if (isset($details[$sortedkey])) {
173
                $sorteddetails[$sortedkey] = $details[$sortedkey];
174
            }
175
        }
176
 
177
        // Make sure the sorted list includes all of the conditions that were set.
178
        if (count($sorteddetails) < count($details)) {
179
            $exceptiontext = get_class($this->cmcompletion) .'::get_sort_order() is missing one or more completion conditions.' .
180
                ' All custom and standard conditions that apply to this activity must be listed.';
181
            throw new \coding_exception($exceptiontext);
182
        }
183
 
184
        return $sorteddetails;
185
    }
186
 
187
    /**
188
     * Fetches the overall completion state of this course module.
189
     *
190
     * @return int The overall completion state for this course module.
191
     */
192
    public function get_overall_completion(): int {
193
        return (int)$this->completiondata->completionstate;
194
    }
195
 
196
    /**
197
     * Returns whether the overall completion state of this course module should be marked as complete or not.
198
     * This is based on the completion settings of the course module, so when the course module requires a passing grade,
199
     * it will only be marked as complete when the user has passed the course module. Otherwise, it will be marked as complete
200
     * even when the user has failed the course module.
201
     *
202
     * @return bool True when the module can be marked as completed.
203
     */
204
    public function is_overall_complete(): bool {
205
        $completionstates = [];
206
        if ($this->is_manual()) {
207
            $completionstates = [COMPLETION_COMPLETE];
208
        } else if ($this->is_automatic()) {
209
            // Successfull completion states depend on the completion settings.
210
            if (property_exists($this->completiondata, 'customcompletion') && !empty($this->completiondata->customcompletion)) {
211
                // If the module has any failed custom completion rule the state could be COMPLETION_COMPLETE_FAIL.
212
                $completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS];
213
            } else if (isset($this->completiondata->passgrade)) {
214
                // Passing grade is required. Don't mark it as complete when state is COMPLETION_COMPLETE_FAIL.
215
                $completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS];
216
            } else {
217
                // Any grade is required. Mark it as complete even when state is COMPLETION_COMPLETE_FAIL.
218
                $completionstates = [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS, COMPLETION_COMPLETE_FAIL];
219
            }
220
        }
221
 
222
        return in_array($this->get_overall_completion(), $completionstates);
223
    }
224
 
225
    /**
226
     * Whether this activity module has completion enabled.
227
     *
228
     * @return bool
229
     */
230
    public function has_completion(): bool {
231
        return $this->completioninfo->is_enabled($this->cminfo) != COMPLETION_DISABLED;
232
    }
233
 
234
    /**
235
     * Whether this activity module instance tracks completion automatically.
236
     *
237
     * @return bool
238
     */
239
    public function is_automatic(): bool {
240
        return $this->cminfo->completion == COMPLETION_TRACKING_AUTOMATIC;
241
    }
242
 
243
    /**
244
     * Whether this activity module instance tracks completion manually.
245
     *
246
     * @return bool
247
     */
248
    public function is_manual(): bool {
249
        return $this->cminfo->completion == COMPLETION_TRACKING_MANUAL;
250
    }
251
 
252
    /**
253
     * Fetches the user ID that has overridden the completion state of this activity for the user.
254
     *
255
     * @return int|null
256
     */
257
    public function overridden_by(): ?int {
258
        return isset($this->completiondata->overrideby) ? (int)$this->completiondata->overrideby : null;
259
    }
260
 
261
    /**
262
     * Checks whether completion is being tracked for this user.
263
     *
264
     * @return bool
265
     */
266
    public function is_tracked_user(): bool {
267
        return $this->completioninfo->is_tracked_user($this->userid);
268
    }
269
 
270
    /**
271
     * Determine whether to show the manual completion or not.
272
     *
273
     * @return bool
274
     */
275
    public function show_manual_completion(): bool {
276
        global $PAGE;
277
 
278
        if (!$this->is_manual()) {
279
            return false;
280
        }
281
 
282
        if ($PAGE->context->contextlevel == CONTEXT_MODULE) {
283
            // Manual completion should always be shown on the activity page.
284
            return true;
285
        } else {
286
            $course = $this->cminfo->get_course();
287
            if ($course->showcompletionconditions == COMPLETION_SHOW_CONDITIONS) {
288
                return true;
289
            } else if ($this->cmcompletion) {
290
                return $this->cmcompletion->manual_completion_always_shown();
291
            }
292
        }
293
 
294
        return false;
295
    }
296
 
297
    /**
298
     * Completion state timemodified
299
     *
300
     * @return int timestamp
301
     */
302
    public function get_timemodified(): int {
303
        return (int)$this->completiondata->timemodified;
304
    }
305
 
306
    /**
307
     * Generates an instance of this class.
308
     *
309
     * @param cm_info $cminfo The course module info instance.
310
     * @param int $userid The user ID that we're fetching completion details for.
311
     * @param bool $returndetails  Whether to return completion details or not.
312
     * @return cm_completion_details
313
     */
314
    public static function get_instance(cm_info $cminfo, int $userid, bool $returndetails = true): cm_completion_details {
315
        $course = $cminfo->get_course();
316
        $completioninfo = new \completion_info($course);
317
        return new self($completioninfo, $cminfo, $userid, $returndetails);
318
    }
319
}