Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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
namespace core_contentbank;
18
 
19
use core_plugin_manager;
20
use stored_file;
21
use context;
22
 
23
/**
24
 * Content bank class
25
 *
26
 * @package    core_contentbank
27
 * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
28
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29
 */
30
class contentbank {
31
 
32
    /** @var array All the context levels allowed in the content bank */
33
    private const ALLOWED_CONTEXT_LEVELS = [CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE];
34
 
35
    /** @var array Enabled content types. */
36
    private $enabledcontenttypes = null;
37
 
38
    /**
39
     * Obtains the list of core_contentbank_content objects currently active.
40
     *
41
     * The list does not include players which are disabled.
42
     *
43
     * @return string[] Array of contentbank contenttypes.
44
     */
45
    public function get_enabled_content_types(): array {
46
        if (!is_null($this->enabledcontenttypes)) {
47
            return $this->enabledcontenttypes;
48
        }
49
 
50
        $enabledtypes = \core\plugininfo\contenttype::get_enabled_plugins();
51
        $types = [];
52
        foreach ($enabledtypes as $name) {
53
            $contenttypeclassname = "\\contenttype_$name\\contenttype";
54
            $contentclassname = "\\contenttype_$name\\content";
55
            if (class_exists($contenttypeclassname) && class_exists($contentclassname)) {
56
                $types[$contenttypeclassname] = $name;
57
            }
58
        }
59
        return $this->enabledcontenttypes = $types;
60
    }
61
 
62
    /**
63
     * Obtains an array of supported extensions by active plugins.
64
     *
65
     * @return array The array with all the extensions supported and the supporting plugin names.
66
     */
67
    public function load_all_supported_extensions(): array {
68
        $extensionscache = \cache::make('core', 'contentbank_enabled_extensions');
69
        $supportedextensions = $extensionscache->get('enabled_extensions');
70
        if ($supportedextensions === false) {
71
            // Load all enabled extensions.
72
            $supportedextensions = [];
73
            foreach ($this->get_enabled_content_types() as $type) {
74
                $classname = "\\contenttype_$type\\contenttype";
75
                $contenttype = new $classname;
76
                if ($contenttype->is_feature_supported($contenttype::CAN_UPLOAD)) {
77
                    $extensions = $contenttype->get_manageable_extensions();
78
                    foreach ($extensions as $extension) {
79
                        if (array_key_exists($extension, $supportedextensions)) {
80
                            $supportedextensions[$extension][] = $type;
81
                        } else {
82
                            $supportedextensions[$extension] = [$type];
83
                        }
84
                    }
85
                }
86
            }
87
            $extensionscache->set('enabled_extensions', $supportedextensions);
88
        }
89
        return $supportedextensions;
90
    }
91
 
92
    /**
93
     * Obtains an array of supported extensions in the given context.
94
     *
95
     * @param context $context Optional context to check (default null)
96
     * @return array The array with all the extensions supported and the supporting plugin names.
97
     */
98
    public function load_context_supported_extensions(context $context = null): array {
99
        $extensionscache = \cache::make('core', 'contentbank_context_extensions');
100
 
101
        $contextextensions = $extensionscache->get($context->id);
102
        if ($contextextensions === false) {
103
            $contextextensions = [];
104
            $supportedextensions = $this->load_all_supported_extensions();
105
            foreach ($supportedextensions as $extension => $types) {
106
                foreach ($types as $type) {
107
                    $classname = "\\contenttype_$type\\contenttype";
108
                    $contenttype = new $classname($context);
109
                    if ($contenttype->can_upload()) {
110
                        $contextextensions[$extension] = $type;
111
                        break;
112
                    }
113
                }
114
            }
115
            $extensionscache->set($context->id, $contextextensions);
116
        }
117
        return $contextextensions;
118
    }
119
 
120
    /**
121
     * Obtains a string with all supported extensions by active plugins.
122
     * Mainly to use as filepicker options parameter.
123
     *
124
     * @param context $context   Optional context to check (default null)
125
     * @return string A string with all the extensions supported.
126
     */
127
    public function get_supported_extensions_as_string(context $context = null) {
128
        $supported = $this->load_context_supported_extensions($context);
129
        $extensions = array_keys($supported);
130
        return implode(',', $extensions);
131
    }
132
 
133
    /**
134
     * Returns the file extension for a file.
135
     *
136
     * @param  string $filename The name of the file
137
     * @return string The extension of the file
138
     */
139
    public function get_extension(string $filename) {
140
        $dot = strrpos($filename, '.');
141
        if ($dot === false) {
142
            return '';
143
        }
144
        return strtolower(substr($filename, $dot));
145
    }
146
 
147
    /**
148
     * Get the first content bank plugin supports a file extension.
149
     *
150
     * @param string $extension Content file extension
151
     * @param context $context $context     Optional context to check (default null)
152
     * @return string contenttype name supports the file extension or null if the extension is not supported by any allowed plugin.
153
     */
154
    public function get_extension_supporter(string $extension, context $context = null): ?string {
155
        $supporters = $this->load_context_supported_extensions($context);
156
        if (array_key_exists($extension, $supporters)) {
157
            return $supporters[$extension];
158
        }
159
        return null;
160
    }
161
 
162
    /**
163
     * Find the contents with %$search% in the contextid defined.
164
     * If contextid and search are empty, all contents are returned.
165
     * In all the cases, only the contents for the enabled contentbank-type plugins are returned.
166
     * No content-type permissions are validated here. It is the caller responsability to check that the user can access to them.
167
     * The only validation done here is, for each content, a call to the method $content->is_view_allowed().
168
     *
169
     * @param  string|null $search Optional string to search (for now it will search only into the name).
170
     * @param  int $contextid Optional contextid to search.
171
     * @param  array $contenttypenames Optional array with the list of content-type names to search.
172
     * @return array The contents for the enabled contentbank-type plugins having $search as name and placed in $contextid.
173
     */
174
    public function search_contents(?string $search = null, ?int $contextid = 0, ?array $contenttypenames = null): array {
175
        global $DB;
176
 
177
        $contents = [];
178
 
179
        // Get only contents for enabled content-type plugins.
180
        $contenttypes = [];
181
        $enabledcontenttypes = $this->get_enabled_content_types();
182
        foreach ($enabledcontenttypes as $contenttypename) {
183
            if (empty($contenttypenames) || in_array($contenttypename, $contenttypenames)) {
184
                $contenttypes[] = "contenttype_$contenttypename";
185
            }
186
        }
187
 
188
        if (empty($contenttypes)) {
189
            // Early return if there are no content-type plugins enabled.
190
            return $contents;
191
        }
192
 
193
        list($sqlcontenttypes, $params) = $DB->get_in_or_equal($contenttypes, SQL_PARAMS_NAMED);
194
        $sql = " contenttype $sqlcontenttypes ";
195
 
196
        // Filter contents on this context (if defined).
197
        if (!empty($contextid)) {
198
            $params['contextid'] = $contextid;
199
            $sql .= ' AND contextid = :contextid ';
200
        }
201
 
202
        // Search for contents having this string (if defined).
203
        if (!empty($search)) {
204
            $sql .= ' AND ' . $DB->sql_like('name', ':name', false, false);
205
            $params['name'] = '%' . $DB->sql_like_escape($search) . '%';
206
        }
207
 
208
        $records = $DB->get_records_select('contentbank_content', $sql, $params, 'name ASC');
209
        foreach ($records as $record) {
210
            $content = $this->get_content_from_id($record->id);
211
            if ($content->is_view_allowed()) {
212
                $contents[] = $content;
213
            }
214
        }
215
 
216
        return $contents;
217
    }
218
 
219
 
220
    /**
221
     * Return all the context where a user has all the given capabilities.
222
     *
223
     * @param  string $capability The capability the user needs to have.
224
     * @param  int|null $userid Optional userid. $USER by default.
225
     * @return array Array of the courses and course categories where the user has the given capability.
226
     */
227
    public function get_contexts_with_capabilities_by_user($capability = 'moodle/contentbank:access', $userid = null): array {
228
        global $USER;
229
 
230
        if (!$userid) {
231
            $userid = $USER->id;
232
        }
233
 
234
        $categoriescache = \cache::make('core', 'contentbank_allowed_categories');
235
        $coursescache = \cache::make('core', 'contentbank_allowed_courses');
236
 
237
        $categories = $categoriescache->get($userid);
238
        $courses = $coursescache->get($userid);
239
 
240
        if ($categories === false || $courses === false) {
11 efrain 241
            // Required fields for preloading the context record.
242
            $contextfields = 'ctxid, ctxpath, ctxdepth, ctxlevel, ctxinstance, ctxlocked';
243
 
1 efrain 244
            list($categories, $courses) = get_user_capability_contexts($capability, true, $userid, true,
11 efrain 245
                "fullname, {$contextfields}", "name, {$contextfields}", 'fullname', 'name');
1 efrain 246
            $categoriescache->set($userid, $categories);
247
            $coursescache->set($userid, $courses);
248
        }
249
 
250
        return [$categories, $courses];
251
    }
252
 
253
    /**
254
     * Create content from a file information.
255
     *
256
     * @param \context $context Context where to upload the file and content.
257
     * @param int $userid Id of the user uploading the file.
258
     * @param stored_file $file The file to get information from
259
     * @return content
260
     */
261
    public function create_content_from_file(\context $context, int $userid, stored_file $file): ?content {
262
        global $USER;
263
        if (empty($userid)) {
264
            $userid = $USER->id;
265
        }
266
        // Get the contenttype to manage given file's extension.
267
        $filename = $file->get_filename();
268
        $extension = $this->get_extension($filename);
269
        $plugin = $this->get_extension_supporter($extension, $context);
270
        $classname = '\\contenttype_'.$plugin.'\\contenttype';
271
        $record = new \stdClass();
272
        $record->name = $filename;
273
        $record->usercreated = $userid;
274
        $contentype = new $classname($context);
275
        $content = $contentype->upload_content($file, $record);
276
        $event = \core\event\contentbank_content_uploaded::create_from_record($content->get_content());
277
        $event->trigger();
278
        return $content;
279
    }
280
 
281
    /**
282
     * Delete content bank content by context.
283
     *
284
     * @param context $context The context to delete content from.
285
     * @return bool
286
     */
287
    public function delete_contents(context $context): bool {
288
        global $DB;
289
 
290
        $result = true;
291
        $records = $DB->get_records('contentbank_content', ['contextid' => $context->id]);
292
        foreach ($records as $record) {
293
            $content = $this->get_content_from_id($record->id);
294
            $contenttype = $content->get_content_type_instance();
295
            if (!$contenttype->delete_content($content)) {
296
                $result = false;
297
            }
298
        }
299
        return $result;
300
    }
301
 
302
    /**
303
     * Move content bank content from a context to another.
304
     *
305
     * @param context $from The context to get content from.
306
     * @param context $to The context to move content to.
307
     * @return bool
308
     */
309
    public function move_contents(context $from, context $to): bool {
310
        global $DB;
311
 
312
        $result = true;
313
        $records = $DB->get_records('contentbank_content', ['contextid' => $from->id]);
314
        foreach ($records as $record) {
315
            $content = $this->get_content_from_id($record->id);
316
            $contenttype = $content->get_content_type_instance();
317
            if (!$contenttype->move_content($content, $to)) {
318
                $result = false;
319
            }
320
        }
321
        return $result;
322
    }
323
 
324
    /**
325
     * Get the list of content types that have the requested feature.
326
     *
327
     * @param string $feature Feature code e.g CAN_UPLOAD.
328
     * @param null|\context $context Optional context to check the permission to use the feature.
329
     * @param bool $enabled Whether check only the enabled content types or all of them.
330
     *
331
     * @return string[] List of content types where the user has permission to access the feature.
332
     */
333
    public function get_contenttypes_with_capability_feature(string $feature, \context $context = null, bool $enabled = true): array {
334
        $contenttypes = [];
335
        // Check enabled content types or all of them.
336
        if ($enabled) {
337
            $contenttypestocheck = $this->get_enabled_content_types();
338
        } else {
339
            $plugins = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
340
            foreach ($plugins as $plugin) {
341
                $contenttypeclassname = "\\{$plugin->type}_{$plugin->name}\\contenttype";
342
                $contenttypestocheck[$contenttypeclassname] = $plugin->name;
343
            }
344
        }
345
 
346
        foreach ($contenttypestocheck as $classname => $name) {
347
            $contenttype = new $classname($context);
348
            // The method names that check the features permissions must follow the pattern can_feature.
349
            if ($contenttype->{"can_$feature"}()) {
350
                $contenttypes[$classname] = $name;
351
            }
352
        }
353
 
354
        return $contenttypes;
355
    }
356
 
357
    /**
358
     * Return a content class form a content id.
359
     *
360
     * @throws coding_exception if the ID is not valid or some class does no exists
361
     * @param int $id the content id
362
     * @return content the content class instance
363
     */
364
    public function get_content_from_id(int $id): content {
365
        global $DB;
366
        $record = $DB->get_record('contentbank_content', ['id' => $id], '*', MUST_EXIST);
367
        $contentclass = "\\$record->contenttype\\content";
368
        return new $contentclass($record);
369
    }
370
 
371
    /**
372
     * Whether the context is allowed.
373
     *
374
     * @param context $context Context to check.
375
     * @return bool
376
     */
377
    public function is_context_allowed(context $context): bool {
378
        return in_array($context->contextlevel, self::ALLOWED_CONTEXT_LEVELS);
379
    }
380
}