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
 * Library providing functions that implement the module's features.
19
 *
20
 * @package     mod_subcourse
21
 * @copyright   2017 David Mudrák <david@moodle.com>
22
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
defined('MOODLE_INTERNAL') || die();
26
 
27
require_once($CFG->libdir.'/gradelib.php');
28
 
29
/**
30
 * Returns the list of courses the grades can be taken from
31
 *
32
 * Returned are courses in which the user has permission to view the grade
33
 * book. Never returns the current course (as a course cannot be a subcourse of
34
 * itself) and the site course (the front page course). If the userid is not
35
 * passed, the current user is expected.
36
 *
37
 * @param int $userid Id of user for which we want to get the list of courses
38
 * @return array list of course records
39
 */
40
function subcourse_available_courses($userid = null) {
41
    global $COURSE, $USER;
42
 
43
    $courses = [];
44
 
45
    if (empty($userid)) {
46
        $userid = $USER->id;
47
    }
48
 
49
    $fields = 'fullname,shortname,idnumber,category,visible,sortorder';
50
    $mycourses = get_user_capability_course('moodle/grade:viewall', $userid, true, $fields, 'sortorder');
51
 
52
    if ($mycourses) {
53
        $ignorecourses = [$COURSE->id, SITEID];
54
        foreach ($mycourses as $mycourse) {
55
            if (in_array($mycourse->id, $ignorecourses)) {
56
                continue;
57
            }
58
            $courses[] = $mycourse;
59
        }
60
    }
61
 
62
    return $courses;
63
}
64
 
65
/**
66
 * Fetches grade_item info and grades from the referenced course
67
 *
68
 * Returned structure is
69
 *  object(
70
 *      ->grades = array[userid] of object(->userid ->rawgrade ->feedback ->feedbackformat ->hidden)
71
 *      ->grademax
72
 *      ->grademin
73
 *      ->itemname
74
 *      ...
75
 *  )
76
 *
77
 * @param int $subcourseid ID of subcourse instance
78
 * @param int $refcourseid ID of referenced course
79
 * @param bool $gradeitemonly If true, fetch only grade item info without grades
80
 * @param int|array $userids If fetching grades, limit only to this user(s), defaults to all.
81
 * @param bool $fetchpercentage Re-calculate the grade value so that the displayed percentage matches the original.
82
 * @return stdClass containing grades array and gradeitem info
83
 */
84
function subcourse_fetch_refgrades($subcourseid, $refcourseid, $gradeitemonly = false, $userids = [], $fetchpercentage = false) {
85
 
86
    if (empty($refcourseid)) {
87
        throw new coding_exception('Empty referenced course id');
88
    }
89
 
90
    $fetchedfields = subcourse_get_fetched_item_fields();
91
 
92
    $return = new stdClass();
93
    $return->grades = [];
94
 
95
    $refgradeitem = grade_item::fetch_course_item($refcourseid);
96
 
97
    // Get grade_item info.
98
    foreach ($fetchedfields as $property) {
99
        if (isset($refgradeitem->$property)) {
100
            $return->$property = $refgradeitem->$property;
101
        } else {
102
            $return->$property = null;
103
        }
104
    }
105
 
106
    // If the remote grade_item is non-global scale, do not fetch grades - they can't be used.
107
    if (($refgradeitem->gradetype == GRADE_TYPE_SCALE) && (!subcourse_is_global_scale($refgradeitem->scaleid))) {
108
        $gradeitemonly = true;
109
        debugging(get_string('errlocalremotescale', 'subcourse'));
110
        $return->localremotescale = true;
111
    }
112
 
113
    if (!$gradeitemonly) {
114
        // Get grades.
115
 
116
        if (!is_array($userids)) {
117
            $userids = [$userids];
118
        }
119
 
120
        $cm = get_coursemodule_from_instance("subcourse", $subcourseid);
121
        $context = context_module::instance($cm->id);
122
 
123
        $users = get_users_by_capability($context, 'mod/subcourse:begraded', 'u.id,u.lastname',
124
                                         'u.lastname', '', '', '', '', false, true);
125
 
126
        foreach ($users as $user) {
127
            if ($userids && !in_array($user->id, $userids)) {
128
                continue;
129
            }
130
 
131
            $grade = new grade_grade(['itemid' => $refgradeitem->id, 'userid' => $user->id]);
132
 
133
            $return->grades[$user->id] = new stdClass();
134
            $return->grades[$user->id]->userid = $user->id;
135
            $return->grades[$user->id]->feedback = $grade->feedback;
136
            $return->grades[$user->id]->feedbackformat = $grade->feedbackformat;
137
            $return->grades[$user->id]->hidden = $grade->hidden;
138
 
139
            if ($grade->finalgrade === null) {
140
                // No grade set yet.
141
                $return->grades[$user->id]->rawgrade = null;
142
 
143
            } else if (empty($fetchpercentage)) {
144
                // Fetch the raw value of the final grade in the referenced course.
145
                $return->grades[$user->id]->rawgrade = $grade->finalgrade;
146
 
147
            } else {
148
                // Re-calculate the value so that the displayed percentage matches.
149
                // This may make difference when there are excluded grades in the referenced course.
150
                if ($grade->rawgrademax > 0) {
151
                    $ratio = ($grade->finalgrade - $grade->rawgrademin) / ($grade->rawgrademax - $grade->rawgrademin);
152
                    $fakevalue = $return->grademin + $ratio * ($return->grademax - $return->grademin);
153
                    $return->grades[$user->id]->rawgrade = grade_floatval($fakevalue);
154
 
155
                } else {
156
                    $return->grades[$user->id]->rawgrade = 0;
157
                }
158
            }
159
        }
160
    }
161
 
162
    return $return;
163
}
164
 
165
/**
166
 * Create or update grade item and grades for given subcourse
167
 *
168
 * @param int $courseid     ID of referencing course (the course containing the instance of
169
 * subcourse)
170
 * @param int $subcourseid  ID of subcourse instance
171
 * @param int $refcourseid  ID of referenced course (the course to take grades from)
172
 * @param str $itemname     Set the itemname
173
 * @param bool $gradeitemonly If true, fetch only grade item info without grades
174
 * @param bool $reset Reset grades in gradebook
175
 * @param int|array $userids If fetching grades, limit only to this user(s), defaults to all.
176
 * @param bool $fetchpercentage Re-calculate the grade value so that the displayed percentage matches the original.
177
 * @return int GRADE_UPDATE_OK etc
178
 */
179
function subcourse_grades_update($courseid, $subcourseid, $refcourseid, $itemname = null,
180
        $gradeitemonly = false, $reset = false, $userids = [], $fetchpercentage = null) {
181
    global $DB;
182
 
183
    if (empty($refcourseid)) {
184
        return GRADE_UPDATE_FAILED;
185
    }
186
 
187
    if (!$DB->record_exists('course', ['id' => $refcourseid])) {
188
        return GRADE_UPDATE_FAILED;
189
    }
190
 
191
    if (!$gradeitemonly && $fetchpercentage === null) {
192
        debugging('Performance: The caller should provide the fetchpercentage value to avoid an extra DB call.', DEBUG_DEVELOPER);
193
        $fetchpercentage = $DB->get_field('subcourse', 'fetchpercentage', ['id' => $subcourseid]);
194
    }
195
 
196
    $fetchedfields = subcourse_get_fetched_item_fields();
197
 
198
    $refgrades = subcourse_fetch_refgrades($subcourseid, $refcourseid, $gradeitemonly, $userids, $fetchpercentage);
199
 
200
    if (!empty($refgrades->localremotescale)) {
201
        // Unable to fetch remote grades - local scale is used in the remote course.
202
        return GRADE_UPDATE_FAILED;
203
    }
204
 
205
    $params = [];
206
 
207
    foreach ($fetchedfields as $property) {
208
        if (isset($refgrades->$property)) {
209
            $params[$property] = $refgrades->$property;
210
        }
211
    }
212
    if (!empty($itemname)) {
213
        $params['itemname'] = $itemname;
214
    }
215
 
216
    $grades = $refgrades->grades;
217
 
218
    if ($reset) {
219
        $params['reset'] = true;
220
        $grades = null;
221
    }
222
 
223
    $result = grade_update('mod/subcourse', $courseid, 'mod', 'subcourse', $subcourseid, 0, $grades, $params);
224
 
225
    // The {@see grade_update()} does not change the grade hidden state so we need to perform it manually now.
226
    if (!$gradeitemonly && $result == GRADE_UPDATE_OK) {
227
        $gi = grade_item::fetch([
228
            'source' => 'mod/subcourse',
229
            'courseid' => $courseid,
230
            'itemtype' => 'mod',
231
            'itemmodule' => 'subcourse',
232
            'iteminstance' => $subcourseid,
233
            'itemnumber' => 0
234
        ]);
235
 
236
        $gs = grade_grade::fetch_all(['itemid' => $gi->id]);
237
 
238
        if (!empty($gs)) {
239
            foreach ($gs as $g) {
240
                if (isset($refgrades->grades[$g->userid])) {
241
                    if ($refgrades->grades[$g->userid]->hidden != $g->hidden) {
242
                        $g->grade_item = $gi;
243
                        $g->set_hidden($refgrades->grades[$g->userid]->hidden);
244
                    }
245
                }
246
            }
247
        }
248
    }
249
 
250
    return $result;
251
}
252
 
253
/**
254
 * Checks if a remote scale can be re-used, i.e. if it is global (standard, server wide) scale
255
 *
256
 * @param mixed $scaleid ID of the scale
257
 * @return boolean True if scale is global, false if not.
258
 */
259
function subcourse_is_global_scale($scaleid) {
260
    global $DB;
261
 
262
    if (!is_numeric($scaleid)) {
263
        throw new moodle_exception('errnonnumeric', 'subcourse');
264
    }
265
 
266
    if (!$DB->get_record('scale', ['id' => $scaleid, 'courseid' => 0], 'id')) {
267
        // No such scale with courseid 0.
268
        return false;
269
    } else {
270
        // Found the global scale.
271
        return true;
272
    }
273
}
274
 
275
/**
276
 * Updates the timefetched timestamp for given subcourses
277
 *
278
 * @param array|int $subcourseids ID of subcourse instance or array of IDs
279
 * @param mixed $time The timestamp, defaults to the current time
280
 * @return bool
281
 */
282
function subcourse_update_timefetched($subcourseids, $time = null) {
283
    global $DB;
284
 
285
    if (empty($subcourseids)) {
286
        return false;
287
    }
288
    if (is_numeric($subcourseids)) {
289
        $subcourseids = [$subcourseids];
290
    }
291
    if (!is_array($subcourseids)) {
292
        return false;
293
    }
294
    if (is_null($time)) {
295
        $time = time();
296
    }
297
    if (!is_numeric($time)) {
298
        return false;
299
    }
300
    list($sql, $params) = $DB->get_in_or_equal($subcourseids);
301
    $DB->set_field_select('subcourse', 'timefetched', $time, "id $sql", $params);
302
 
303
    return true;
304
}
305
 
306
/**
307
 * The list of fields to copy from remote grade_item
308
 * @return array
309
 */
310
function subcourse_get_fetched_item_fields() {
311
    return ['gradetype', 'grademax', 'grademin', 'scaleid', 'hidden'];
312
}
313
 
314
/**
315
 * Return if the user has a grade for the activity and the string representation of the grade.
316
 *
317
 * @param stdClass $subcourse Subcourse activity record with id and course properties set
318
 * @param int $userid User id to get the grade for
319
 * @return string $strgrade
320
 */
321
function subcourse_get_current_grade(stdClass $subcourse, int $userid): ?string {
322
 
323
    $currentgrade = grade_get_grades($subcourse->course, 'mod', 'subcourse', $subcourse->id, $userid);
324
    $strgrade = null;
325
 
326
    if (!empty($currentgrade->items[0]->grades)) {
327
        $currentgrade = reset($currentgrade->items[0]->grades);
328
 
329
        if (isset($currentgrade->grade) && !($currentgrade->hidden)) {
330
            $strgrade = $currentgrade->str_grade;
331
        }
332
    }
333
 
334
    return $strgrade;
335
}
336
 
337
/**
338
 * Mark the course module as viewed by the user.
339
 *
340
 * @param stdClass $subcourse Subcourse record.
341
 * @param context $context Course module context.
342
 * @param stdClass $course Course record.
343
 * @param cm_info|object $cm Course module info.
344
 */
345
function subcourse_set_module_viewed(stdClass $subcourse, context $context, stdClass $course, $cm) {
346
    global $CFG;
347
    require_once($CFG->libdir . '/completionlib.php');
348
 
349
    $completion = new completion_info($course);
350
    $completion->set_module_viewed($cm);
351
 
352
    $event = \mod_subcourse\event\course_module_viewed::create([
353
        'objectid' => $subcourse->id,
354
        'context' => $context,
355
    ]);
356
 
357
    $event->add_record_snapshot('course_modules', $cm);
358
    $event->add_record_snapshot('course', $course);
359
    $event->add_record_snapshot('subcourse', $subcourse);
360
 
361
    $event->trigger();
362
}