e34dfa25f7153e33db9e9cd5b699995145dff635
[moodle.git] / contentbank / classes / contentbank.php
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/>.
17 /**
18  * Content bank class
19  *
20  * @package    core_contentbank
21  * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace core_contentbank;
27 use stored_file;
29 /**
30  * Content bank class
31  *
32  * @package    core_contentbank
33  * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
34  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35  */
36 class contentbank {
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     private function get_enabled_content_types(): array {
46         $enabledtypes = \core\plugininfo\contenttype::get_enabled_plugins();
47         $types = [];
48         foreach ($enabledtypes as $name) {
49             $contenttypeclassname = "\\contenttype_$name\\contenttype";
50             $contentclassname = "\\contenttype_$name\\content";
51             if (class_exists($contenttypeclassname) && class_exists($contentclassname)) {
52                 $types[] = $name;
53             }
54         }
55         return $types;
56     }
58     /**
59      * Obtains an array of supported extensions by active plugins.
60      *
61      * @return array The array with all the extensions supported and the supporting plugin names.
62      */
63     public function load_all_supported_extensions(): array {
64         $extensionscache = \cache::make('core', 'contentbank_enabled_extensions');
65         $supportedextensions = $extensionscache->get('enabled_extensions');
66         if ($supportedextensions === false) {
67             // Load all enabled extensions.
68             $supportedextensions = [];
69             foreach ($this->get_enabled_content_types() as $type) {
70                 $classname = "\\contenttype_$type\\contenttype";
71                 $contenttype = new $classname;
72                 if ($contenttype->is_feature_supported($contenttype::CAN_UPLOAD)) {
73                     $extensions = $contenttype->get_manageable_extensions();
74                     foreach ($extensions as $extension) {
75                         if (array_key_exists($extension, $supportedextensions)) {
76                             $supportedextensions[$extension][] = $type;
77                         } else {
78                             $supportedextensions[$extension] = [$type];
79                         }
80                     }
81                 }
82             }
83             $extensionscache->set('enabled_extensions', $supportedextensions);
84         }
85         return $supportedextensions;
86     }
88     /**
89      * Obtains an array of supported extensions in the given context.
90      *
91      * @param \context $context Optional context to check (default null)
92      * @return array The array with all the extensions supported and the supporting plugin names.
93      */
94     public function load_context_supported_extensions(\context $context = null): array {
95         $extensionscache = \cache::make('core', 'contentbank_context_extensions');
97         $contextextensions = $extensionscache->get($context->id);
98         if ($contextextensions === false) {
99             $contextextensions = [];
100             $supportedextensions = $this->load_all_supported_extensions();
101             foreach ($supportedextensions as $extension => $types) {
102                 foreach ($types as $type) {
103                     $classname = "\\contenttype_$type\\contenttype";
104                     $contenttype = new $classname($context);
105                     if ($contenttype->can_upload()) {
106                         $contextextensions[$extension] = $type;
107                         break;
108                     }
109                 }
110             }
111             $extensionscache->set($context->id, $contextextensions);
112         }
113         return $contextextensions;
114     }
116     /**
117      * Obtains a string with all supported extensions by active plugins.
118      * Mainly to use as filepicker options parameter.
119      *
120      * @param \context $context   Optional context to check (default null)
121      * @return string A string with all the extensions supported.
122      */
123     public function get_supported_extensions_as_string(\context $context = null) {
124         $supported = $this->load_context_supported_extensions($context);
125         $extensions = array_keys($supported);
126         return implode(',', $extensions);
127     }
129     /**
130      * Returns the file extension for a file.
131      *
132      * @param  string $filename The name of the file
133      * @return string The extension of the file
134      */
135     public function get_extension(string $filename) {
136         $dot = strrpos($filename, '.');
137         if ($dot === false) {
138             return '';
139         }
140         return strtolower(substr($filename, $dot));
141     }
143     /**
144      * Get the first content bank plugin supports a file extension.
145      *
146      * @param string $extension Content file extension
147      * @param \context $context $context     Optional context to check (default null)
148      * @return string contenttype name supports the file extension or null if the extension is not supported by any allowed plugin.
149      */
150     public function get_extension_supporter(string $extension, \context $context = null): ?string {
151         $supporters = $this->load_context_supported_extensions($context);
152         if (array_key_exists($extension, $supporters)) {
153             return $supporters[$extension];
154         }
155         return null;
156     }
158     /**
159      * Find the contents with %$search% in the contextid defined.
160      * If contextid and search are empty, all contents are returned.
161      * In all the cases, only the contents for the enabled contentbank-type plugins are returned.
162      *
163      * @param  string|null $search Optional string to search (for now it will search only into the name).
164      * @param  int $contextid Optional contextid to search.
165      * @return array The contents for the enabled contentbank-type plugins having $search as name and placed in $contextid.
166      */
167     public function search_contents(?string $search = null, ?int $contextid = 0): array {
168         global $DB;
170         $contents = [];
172         // Get only contents for enabled content-type plugins.
173         $contenttypes = array_map(function($contenttypename) {
174             return "contenttype_$contenttypename";
175         }, $this->get_enabled_content_types());
176         if (empty($contenttypes)) {
177             // Early return if there are no content-type plugins enabled.
178             return $contents;
179         }
181         list($sqlcontenttypes, $params) = $DB->get_in_or_equal($contenttypes, SQL_PARAMS_NAMED);
182         $sql = " contenttype $sqlcontenttypes ";
184         // Filter contents on this context (if defined).
185         if (!empty($contextid)) {
186             $params['contextid'] = $contextid;
187             $sql .= ' AND contextid = :contextid ';
188         }
190         // Search for contents having this string (if defined).
191         if (!empty($search)) {
192             $sql .= ' AND ' . $DB->sql_like('name', ':name', false, false);
193             $params['name'] = '%' . $DB->sql_like_escape($search) . '%';
194         }
196         $records = $DB->get_records_select('contentbank_content', $sql, $params, 'name ASC');
197         foreach ($records as $record) {
198             $contentclass = "\\$record->contenttype\\content";
199             $content = new $contentclass($record);
200             if ($content->is_view_allowed()) {
201                 $contents[] = $content;
202             }
203         }
205         return $contents;
206     }
208     /**
209      * Create content from a file information.
210      *
211      * @param \context $context Context where to upload the file and content.
212      * @param int $userid Id of the user uploading the file.
213      * @param stored_file $file The file to get information from
214      * @return content
215      */
216     public function create_content_from_file(\context $context, int $userid, stored_file $file): ?content {
217         global $USER;
218         if (empty($userid)) {
219             $userid = $USER->id;
220         }
221         // Get the contenttype to manage given file's extension.
222         $filename = $file->get_filename();
223         $extension = $this->get_extension($filename);
224         $plugin = $this->get_extension_supporter($extension, $context);
225         $classname = '\\contenttype_'.$plugin.'\\contenttype';
226         $record = new \stdClass();
227         $record->name = $filename;
228         $record->usercreated = $userid;
229         $contentype = new $classname($context);
230         $content = $contentype->create_content($record);
231         $event = \core\event\contentbank_content_uploaded::create_from_record($content->get_content());
232         $event->trigger();
233         return $content;
234     }