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 - https://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 <https://www.gnu.org/licenses/>.
16
 
17
namespace core\context;
18
 
19
use core\context;
20
use stdClass;
21
use coding_exception, moodle_url;
22
 
23
/**
24
 * Course module context class
25
 *
26
 * @package   core_access
27
 * @category  access
28
 * @copyright Petr Skoda
29
 * @license   https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
 * @since     Moodle 4.2
31
 */
32
class module extends context {
33
    /** @var int numeric context level value matching legacy CONTEXT_MODULE */
34
    public const LEVEL = 70;
35
 
36
    /**
37
     * Please use \core\context\module::instance($cmid) if you need the instance of context.
38
     * Alternatively if you know only the context id use \core\context::instance_by_id($contextid)
39
     *
40
     * @param stdClass $record
41
     */
42
    protected function __construct(stdClass $record) {
43
        parent::__construct($record);
44
        if ($record->contextlevel != self::LEVEL) {
45
            throw new coding_exception('Invalid $record->contextlevel in core\context\module constructor.');
46
        }
47
    }
48
 
49
    /**
50
     * Returns short context name.
51
     *
52
     * @since Moodle 4.2
53
     *
54
     * @return string
55
     */
56
    public static function get_short_name(): string {
57
        return 'module';
58
    }
59
 
60
    /**
61
     * Returns human readable context level name.
62
     *
63
     * @return string the human readable context level name.
64
     */
65
    public static function get_level_name() {
66
        return get_string('activitymodule');
67
    }
68
 
69
    /**
70
     * Returns human readable context identifier.
71
     *
72
     * @param boolean $withprefix whether to prefix the name of the context with the
73
     *      module name, e.g. Forum, Glossary, etc.
74
     * @param boolean $short does not apply to module context
75
     * @param boolean $escape Whether the returned name of the context is to be HTML escaped or not.
76
     * @return string the human readable context name.
77
     */
78
    public function get_context_name($withprefix = true, $short = false, $escape = true) {
79
        global $DB;
80
 
81
        $name = '';
82
        if ($cm = $DB->get_record_sql("SELECT cm.*, md.name AS modname
83
                                         FROM {course_modules} cm
84
                                         JOIN {modules} md ON md.id = cm.module
85
                                        WHERE cm.id = ?", array($this->_instanceid))) {
86
            if ($mod = $DB->get_record($cm->modname, array('id' => $cm->instance))) {
87
                if ($withprefix) {
88
                    $name = get_string('modulename', $cm->modname).': ';
89
                }
90
                if (!$escape) {
91
                    $name .= format_string($mod->name, true, array('context' => $this, 'escape' => false));
92
                } else {
93
                    $name .= format_string($mod->name, true, array('context' => $this));
94
                }
95
            }
96
        }
97
        return $name;
98
    }
99
 
100
    /**
101
     * Returns the most relevant URL for this context.
102
     *
103
     * @return moodle_url
104
     */
105
    public function get_url() {
106
        global $DB;
107
 
108
        if ($modname = $DB->get_field_sql("SELECT md.name AS modname
109
                                             FROM {course_modules} cm
110
                                             JOIN {modules} md ON md.id = cm.module
111
                                            WHERE cm.id = ?", array($this->_instanceid))) {
112
            return new moodle_url('/mod/' . $modname . '/view.php', array('id' => $this->_instanceid));
113
        }
114
 
115
        return new moodle_url('/');
116
    }
117
 
118
    /**
119
     * Returns context instance database name.
120
     *
121
     * @return string|null table name for all levels except system.
122
     */
123
    protected static function get_instance_table(): ?string {
124
        return 'course_modules';
125
    }
126
 
127
    /**
128
     * Returns list of columns that can be used from behat
129
     * to look up context by reference.
130
     *
131
     * @return array list of column names from instance table
132
     */
133
    protected static function get_behat_reference_columns(): array {
134
        return ['idnumber'];
135
    }
136
 
137
    /**
138
     * Returns list of all role archetypes that are compatible
139
     * with role assignments in context level.
140
     * @since Moodle 4.2
141
     *
142
     * @return int[]
143
     */
144
    protected static function get_compatible_role_archetypes(): array {
145
        return ['editingteacher', 'teacher', 'student'];
146
    }
147
 
148
    /**
149
     * Returns list of all possible parent context levels.
150
     * @since Moodle 4.2
151
     *
152
     * @return int[]
153
     */
154
    public static function get_possible_parent_levels(): array {
155
        return [course::LEVEL];
156
    }
157
 
158
    /**
159
     * Returns array of relevant context capability records.
160
     *
161
     * @param string $sort
162
     * @return array
163
     */
164
    public function get_capabilities(string $sort = self::DEFAULT_CAPABILITY_SORT) {
165
        global $DB, $CFG;
166
 
167
        $cm = $DB->get_record('course_modules', array('id' => $this->_instanceid));
168
        $module = $DB->get_record('modules', array('id' => $cm->module));
169
 
170
        $subcaps = array();
171
 
172
        $modulepath = "{$CFG->dirroot}/mod/{$module->name}";
173
        if (file_exists("{$modulepath}/db/subplugins.json")) {
174
            $subplugins = (array) json_decode(file_get_contents("{$modulepath}/db/subplugins.json"))->plugintypes;
175
        } else if (file_exists("{$modulepath}/db/subplugins.php")) {
176
            debugging('Use of subplugins.php has been deprecated. ' .
177
                'Please update your plugin to provide a subplugins.json file instead.',
178
                DEBUG_DEVELOPER);
179
            $subplugins = array();  // Should be redefined in the file.
180
            include("{$modulepath}/db/subplugins.php");
181
        }
182
 
183
        if (!empty($subplugins)) {
184
            foreach (array_keys($subplugins) as $subplugintype) {
185
                foreach (array_keys(\core_component::get_plugin_list($subplugintype)) as $subpluginname) {
186
                    $subcaps = array_merge($subcaps, array_keys(load_capability_def($subplugintype.'_'.$subpluginname)));
187
                }
188
            }
189
        }
190
 
191
        $modfile = "{$modulepath}/lib.php";
192
        $extracaps = array();
193
        if (file_exists($modfile)) {
194
            include_once($modfile);
195
            $modfunction = $module->name.'_get_extra_capabilities';
196
            if (function_exists($modfunction)) {
197
                $extracaps = $modfunction();
198
            }
199
        }
200
 
201
        $extracaps = array_merge($subcaps, $extracaps);
202
        $extra = '';
203
        list($extra, $params) = $DB->get_in_or_equal(
204
            $extracaps, SQL_PARAMS_NAMED, 'cap0', true, '');
205
        if (!empty($extra)) {
206
            $extra = "OR name $extra";
207
        }
208
 
209
        // Fetch the list of modules, and remove this one.
210
        $components = \core_component::get_component_list();
211
        $componentnames = $components['mod'];
212
        unset($componentnames["mod_{$module->name}"]);
213
        $componentnames = array_keys($componentnames);
214
 
215
        // Exclude all other modules.
216
        list($notcompsql, $notcompparams) = $DB->get_in_or_equal($componentnames, SQL_PARAMS_NAMED, 'notcomp', false);
217
        $params = array_merge($params, $notcompparams);
218
 
219
        // Exclude other component submodules.
220
        $i = 0;
221
        $ignorecomponents = [];
222
        foreach ($componentnames as $mod) {
223
            if ($subplugins = \core_component::get_subplugins($mod)) {
224
                foreach (array_keys($subplugins) as $subplugintype) {
225
                    $paramname = "notlike{$i}";
226
                    $ignorecomponents[] = $DB->sql_like('component', ":{$paramname}", true, true, true);
227
                    $params[$paramname] = "{$subplugintype}_%";
228
                    $i++;
229
                }
230
            }
231
        }
232
        $notlikesql = "(" . implode(' AND ', $ignorecomponents) . ")";
233
 
234
        $sql = "SELECT *
235
                  FROM {capabilities}
236
                 WHERE (contextlevel = ".self::LEVEL."
237
                   AND component {$notcompsql}
238
                   AND {$notlikesql})
239
                       $extra
240
              ORDER BY $sort";
241
 
242
        return $DB->get_records_sql($sql, $params);
243
    }
244
 
245
    /**
246
     * Is this context part of any course? If yes return course context.
247
     *
248
     * @param bool $strict true means throw exception if not found, false means return false if not found
249
     * @return course|false context of the enclosing course, null if not found or exception
250
     */
251
    public function get_course_context($strict = true) {
252
        return $this->get_parent_context();
253
    }
254
 
255
    /**
256
     * Returns module context instance.
257
     *
258
     * @param int $cmid id of the record from {course_modules} table; pass cmid there, NOT id in the instance column
259
     * @param int $strictness
260
     * @return module|false context instance
261
     */
262
    public static function instance($cmid, $strictness = MUST_EXIST) {
263
        global $DB;
264
 
265
        if ($context = context::cache_get(self::LEVEL, $cmid)) {
266
            return $context;
267
        }
268
 
269
        if (!$record = $DB->get_record('context', array('contextlevel' => self::LEVEL, 'instanceid' => $cmid))) {
270
            if ($cm = $DB->get_record('course_modules', array('id' => $cmid), 'id,course', $strictness)) {
271
                $parentcontext = course::instance($cm->course);
272
                $record = context::insert_context_record(self::LEVEL, $cm->id, $parentcontext->path);
273
            }
274
        }
275
 
276
        if ($record) {
277
            $context = new module($record);
278
            context::cache_add($context);
279
            return $context;
280
        }
281
 
282
        return false;
283
    }
284
 
285
    /**
286
     * Create missing context instances at module context level
287
     */
288
    protected static function create_level_instances() {
289
        global $DB;
290
 
291
        $sql = "SELECT " . self::LEVEL . ", cm.id
292
                  FROM {course_modules} cm
293
                 WHERE NOT EXISTS (SELECT 'x'
294
                                     FROM {context} cx
295
                                    WHERE cm.id = cx.instanceid AND cx.contextlevel=" . self::LEVEL . ")";
296
        $contextdata = $DB->get_recordset_sql($sql);
297
        foreach ($contextdata as $context) {
298
            context::insert_context_record(self::LEVEL, $context->id, null);
299
        }
300
        $contextdata->close();
301
    }
302
 
303
    /**
304
     * Returns sql necessary for purging of stale context instances.
305
     *
306
     * @return string cleanup SQL
307
     */
308
    protected static function get_cleanup_sql() {
309
        $sql = "
310
                  SELECT c.*
311
                    FROM {context} c
312
         LEFT OUTER JOIN {course_modules} cm ON c.instanceid = cm.id
313
                   WHERE cm.id IS NULL AND c.contextlevel = " . self::LEVEL . "
314
               ";
315
 
316
        return $sql;
317
    }
318
 
319
    /**
320
     * Rebuild context paths and depths at module context level.
321
     *
322
     * @param bool $force
323
     */
324
    protected static function build_paths($force) {
325
        global $DB;
326
 
327
        if ($force || $DB->record_exists_select('context', "contextlevel = " . self::LEVEL . " AND (depth = 0 OR path IS NULL)")) {
328
            if ($force) {
329
                $ctxemptyclause = '';
330
            } else {
331
                $ctxemptyclause = "AND (ctx.path IS NULL OR ctx.depth = 0)";
332
            }
333
 
334
            $sql = "INSERT INTO {context_temp} (id, path, depth, locked)
335
                    SELECT ctx.id, ".$DB->sql_concat('pctx.path', "'/'", 'ctx.id').", pctx.depth+1, ctx.locked
336
                      FROM {context} ctx
337
                      JOIN {course_modules} cm ON (cm.id = ctx.instanceid AND ctx.contextlevel = " . self::LEVEL . ")
338
                      JOIN {context} pctx ON (pctx.instanceid = cm.course AND pctx.contextlevel = " . course::LEVEL . ")
339
                     WHERE pctx.path IS NOT NULL AND pctx.depth > 0
340
                           $ctxemptyclause";
341
            $trans = $DB->start_delegated_transaction();
342
            $DB->delete_records('context_temp');
343
            $DB->execute($sql);
344
            context::merge_context_temp_table();
345
            $DB->delete_records('context_temp');
346
            $trans->allow_commit();
347
        }
348
    }
349
}