MDL-67795 contentbank: delete content API
[moodle.git] / contentbank / classes / contenttype.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 type manager 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 coding_exception;
28 use moodle_url;
30 /**
31  * Content type manager class
32  *
33  * @package    core_contentbank
34  * @copyright  2020 Amaia Anabitarte <amaia@moodle.com>
35  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36  */
37 abstract class contenttype {
39     /** Plugin implements uploading feature */
40     const CAN_UPLOAD = 'upload';
42     /** @var context This content's context. **/
43     protected $context = null;
45     /**
46      * Content type constructor
47      *
48      * @param \context $context Optional context to check (default null)
49      */
50     public function __construct(\context $context = null) {
51         if (empty($context)) {
52             $context = \context_system::instance();
53         }
54         $this->context = $context;
55     }
57     /**
58      * Fills content_bank table with appropiate information.
59      *
60      * @param stdClass $content  An optional content record compatible object (default null)
61      * @return content       Object with content bank information.
62      */
63     public function create_content(\stdClass $content = null): ?content {
64         global $USER, $DB;
66         $record = new \stdClass();
67         $record->contenttype = $this->get_contenttype_name();
68         $record->contextid = $this->context->id;
69         $record->name = $content->name ?? '';
70         $record->usercreated = $content->usercreated ?? $USER->id;
71         $record->timecreated = time();
72         $record->usermodified = $record->usercreated;
73         $record->timemodified = $record->timecreated;
74         $record->configdata = $content->configdata ?? '';
75         $record->id = $DB->insert_record('contentbank_content', $record);
76         if ($record->id) {
77             $classname = '\\'.$record->contenttype.'\\content';
78             return new $classname($record);
79         }
80         return null;
81     }
83     /**
84      * Delete this content from the content_bank.
85      * This method can be overwritten by the plugins if they need to delete specific information.
86      *
87      * @param  content $content The content to delete.
88      * @return boolean true if the content has been deleted; false otherwise.
89      */
90     public function delete_content(content $content): bool {
91         global $DB;
93         // Delete the file if it exists.
94         if ($file = $content->get_file()) {
95             $file->delete();
96         }
98         // Delete the contentbank DB entry.
99         return $DB->delete_records('contentbank_content', ['id' => $content->get_id()]);
100     }
102     /**
103      * Returns the contenttype name of this content.
104      *
105      * @return string   Content type of the current instance
106      */
107     public function get_contenttype_name(): string {
108         $classname = get_class($this);
109         $contenttype = explode('\\', $classname);
110         return array_shift($contenttype);
111     }
113     /**
114      * Returns the plugin name of the current instance.
115      *
116      * @return string   Plugin name of the current instance
117      */
118     public function get_plugin_name(): string {
119         $contenttype = $this->get_contenttype_name();
120         $plugin = explode('_', $contenttype);
121         return array_pop($plugin);
122     }
124     /**
125      * Returns the URL where the content will be visualized.
126      *
127      * @param stdClass $record  Th content to be displayed.
128      * @return string            URL where to visualize the given content.
129      */
130     public function get_view_url(\stdClass $record): string {
131         return new moodle_url('/contentbank/view.php', ['id' => $record->id]);
132     }
134     /**
135      * Returns the HTML content to add to view.php visualizer.
136      *
137      * @param stdClass $record  Th content to be displayed.
138      * @return string            HTML code to include in view.php.
139      */
140     public function get_view_content(\stdClass $record): string {
141         // Main contenttype class can visualize the content, but plugins could overwrite visualization.
142         return '';
143     }
145     /**
146      * Returns the HTML code to render the icon for content bank contents.
147      *
148      * @param string $contentname   The contentname to add as alt value to the icon.
149      * @return string               HTML code to render the icon
150      */
151     public function get_icon(string $contentname): string {
152         global $OUTPUT;
153         return $OUTPUT->pix_icon('f/unknown-64', $contentname, 'moodle', ['class' => 'iconsize-big']);
154     }
156     /**
157      * Returns user has access capability for the main content bank and the content itself (base on is_access_allowed from plugin).
158      *
159      * @return bool     True if content could be accessed. False otherwise.
160      */
161     final public function can_access(): bool {
162         $classname = 'contenttype/'.$this->get_plugin_name();
163         $capability = $classname.":access";
164         $hascapabilities = has_capability('moodle/contentbank:access', $this->context)
165             && has_capability($capability, $this->context);
166         return $hascapabilities && $this->is_access_allowed();
167     }
169     /**
170      * Returns user has access capability for the content itself.
171      *
172      * @return bool     True if content could be accessed. False otherwise.
173      */
174     protected function is_access_allowed(): bool {
175         // Plugins can overwrite this function to add any check they need.
176         return true;
177     }
179     /**
180      * Returns the user has permission to upload new content.
181      *
182      * @return bool     True if content could be uploaded. False otherwise.
183      */
184     final public function can_upload(): bool {
185         if (!$this->is_feature_supported(self::CAN_UPLOAD)) {
186             return false;
187         }
188         if (!$this->can_access()) {
189             return false;
190         }
192         $classname = 'contenttype/'.$this->get_plugin_name();
193         $uploadcap = $classname.':upload';
194         $hascapabilities = has_capability('moodle/contentbank:upload', $this->context)
195             && has_capability($uploadcap, $this->context);
196         return $hascapabilities && $this->is_upload_allowed();
197     }
199     /**
200      * Returns plugin allows uploading.
201      *
202      * @return bool     True if plugin allows uploading. False otherwise.
203      */
204     protected function is_upload_allowed(): bool {
205         // Plugins can overwrite this function to add any check they need.
206         return true;
207     }
209     /**
210      * Check if the user can delete this content.
211      *
212      * @param  content $content The content to be deleted.
213      * @return bool True if content could be uploaded. False otherwise.
214      */
215     final public function can_delete(content $content): bool {
216         global $USER;
218         if ($this->context->id != $content->get_content()->contextid) {
219             // The content has to have exactly the same context as this contenttype.
220             return false;
221         }
223         $hascapability = has_capability('moodle/contentbank:deleteanycontent', $this->context);
224         if ($content->get_content()->usercreated == $USER->id) {
225             // This content has been created by the current user; check if she can delete her content.
226             $hascapability = $hascapability || has_capability('moodle/contentbank:deleteowncontent', $this->context);
227         }
229         return $hascapability && $this->is_delete_allowed($content);
230     }
232     /**
233      * Returns if content allows deleting.
234      *
235      * @param  content $content The content to be deleted.
236      * @return bool True if content allows uploading. False otherwise.
237      */
238     protected function is_delete_allowed(content $content): bool {
239         // Plugins can overwrite this function to add any check they need.
240         return true;
241     }
243     /**
244      * Returns the plugin supports the feature.
245      *
246      * @param string $feature Feature code e.g CAN_UPLOAD
247      * @return bool     True if content could be uploaded. False otherwise.
248      */
249     final public function is_feature_supported(string $feature): bool {
250         return in_array($feature, $this->get_implemented_features());
251     }
253     /**
254      * Return an array of implemented features by the plugins.
255      *
256      * @return array
257      */
258     abstract protected function get_implemented_features(): array;
260     /**
261      * Return an array of extensions the plugins could manage.
262      *
263      * @return array
264      */
265     abstract public function get_manageable_extensions(): array;