2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Content manager class
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
25 namespace core_contentbank
;
33 use core\event\contentbank_content_updated
;
36 * Content manager class
38 * @package core_contentbank
39 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42 abstract class content
{
44 * @var int Visibility value. Public content is visible to all users with access to the content bank of the
45 * appropriate context.
47 public const VISIBILITY_PUBLIC
= 1;
50 * @var int Visibility value. Unlisted content is only visible to the author and to users with
51 * moodle/contentbank:viewunlistedcontent capability.
53 public const VISIBILITY_UNLISTED
= 2;
55 /** @var stdClass $content The content of the current instance. **/
56 protected $content = null;
59 * Content bank constructor
61 * @param stdClass $record A contentbank_content record.
62 * @throws coding_exception If content type is not right.
64 public function __construct(stdClass
$record) {
65 // Content type should exist and be linked to plugin classname.
66 $classname = $record->contenttype
.'\\content';
67 if (get_class($this) != $classname) {
68 throw new coding_exception(get_string('contenttypenotfound', 'error', $record->contenttype
));
70 $typeclass = $record->contenttype
.'\\contenttype';
71 if (!class_exists($typeclass)) {
72 throw new coding_exception(get_string('contenttypenotfound', 'error', $record->contenttype
));
74 // A record with the id must exist in 'contentbank_content' table.
75 // To improve performance, we are only checking the id is set, but no querying the database.
76 if (!isset($record->id
)) {
77 throw new coding_exception(get_string('invalidcontentid', 'error'));
79 $this->content
= $record;
83 * Returns $this->content.
85 * @return stdClass $this->content.
87 public function get_content(): stdClass
{
88 return $this->content
;
92 * Returns $this->content->contenttype.
94 * @return string $this->content->contenttype.
96 public function get_content_type(): string {
97 return $this->content
->contenttype
;
101 * Return the contenttype instance of this content.
103 * @return contenttype The content type instance
105 public function get_content_type_instance(): contenttype
{
106 $context = context
::instance_by_id($this->content
->contextid
);
107 $contenttypeclass = "\\{$this->content->contenttype}\\contenttype";
108 return new $contenttypeclass($context);
112 * Returns $this->content->timemodified.
114 * @return int $this->content->timemodified.
116 public function get_timemodified(): int {
117 return $this->content
->timemodified
;
121 * Updates content_bank table with information in $this->content.
123 * @return boolean True if the content has been succesfully updated. False otherwise.
124 * @throws \coding_exception if not loaded.
126 public function update_content(): bool {
129 // A record with the id must exist in 'contentbank_content' table.
130 // To improve performance, we are only checking the id is set, but no querying the database.
131 if (!isset($this->content
->id
)) {
132 throw new coding_exception(get_string('invalidcontentid', 'error'));
134 $this->content
->usermodified
= $USER->id
;
135 $this->content
->timemodified
= time();
136 $result = $DB->update_record('contentbank_content', $this->content
);
138 // Trigger an event for updating this content.
139 $event = contentbank_content_updated
::create_from_record($this->content
);
146 * Set a new name to the content.
148 * @param string $name The name of the content.
149 * @return bool True if the content has been succesfully updated. False otherwise.
150 * @throws \coding_exception if not loaded.
152 public function set_name(string $name): bool {
159 $name = clean_param($name, PARAM_TEXT
);
160 if (core_text
::strlen($name) > 255) {
161 $name = core_text
::substr($name, 0, 255);
164 $oldname = $this->content
->name
;
165 $this->content
->name
= $name;
166 $updated = $this->update_content();
168 $this->content
->name
= $oldname;
174 * Returns the name of the content.
176 * @return string The name of the content.
178 public function get_name(): string {
179 return $this->content
->name
;
183 * Set a new contextid to the content.
185 * @param int $contextid The new contextid of the content.
186 * @return bool True if the content has been succesfully updated. False otherwise.
188 public function set_contextid(int $contextid): bool {
189 if ($this->content
->contextid
== $contextid) {
193 $oldcontextid = $this->content
->contextid
;
194 $this->content
->contextid
= $contextid;
195 $updated = $this->update_content();
197 // Move files to new context
198 $fs = get_file_storage();
199 $fs->move_area_files_to_new_context($oldcontextid, $contextid, 'contentbank', 'public', $this->content
->id
);
201 $this->content
->contextid
= $oldcontextid;
207 * Returns the contextid of the content.
209 * @return int The id of the content context.
211 public function get_contextid(): string {
212 return $this->content
->contextid
;
216 * Returns the content ID.
218 * @return int The content ID.
220 public function get_id(): int {
221 return $this->content
->id
;
225 * Change the content instanceid value.
227 * @param int $instanceid New instanceid for this content
228 * @return boolean True if the instanceid has been succesfully updated. False otherwise.
230 public function set_instanceid(int $instanceid): bool {
231 $this->content
->instanceid
= $instanceid;
232 return $this->update_content();
236 * Returns the $instanceid of this content.
238 * @return int contentbank instanceid
240 public function get_instanceid(): int {
241 return $this->content
->instanceid
;
245 * Change the content config values.
247 * @param string $configdata New config information for this content
248 * @return boolean True if the configdata has been succesfully updated. False otherwise.
250 public function set_configdata(string $configdata): bool {
251 $this->content
->configdata
= $configdata;
252 return $this->update_content();
256 * Return the content config values.
258 * @return mixed Config information for this content (json decoded)
260 public function get_configdata() {
261 return $this->content
->configdata
;
265 * Sets a new content visibility and saves it to database.
267 * @param int $visibility Must be self::PUBLIC or self::UNLISTED
269 * @throws coding_exception
271 public function set_visibility(int $visibility): bool {
272 if (!in_array($visibility, [self
::VISIBILITY_PUBLIC
, self
::VISIBILITY_UNLISTED
])) {
275 $this->content
->visibility
= $visibility;
276 return $this->update_content();
280 * Return true if the content may be shown to other users in the content bank.
284 public function get_visibility(): int {
285 return $this->content
->visibility
;
289 * Import a file as a valid content.
291 * By default, all content has a public file area to interact with the content bank
292 * repository. This method should be overridden by contentypes which does not simply
293 * upload to the public file area.
295 * If any, the method will return the final stored_file. This way it can be invoked
296 * as parent::import_file in case any plugin want to store the file in the public area
299 * @throws file_exception If file operations fail
300 * @param stored_file $file File to store in the content file area.
301 * @return stored_file|null the stored content file or null if the file is discarted.
303 public function import_file(stored_file
$file): ?stored_file
{
304 $originalfile = $this->get_file();
306 $originalfile->replace_file_with($file);
307 return $originalfile;
309 $itemid = $this->get_id();
310 $fs = get_file_storage();
312 'contextid' => $this->get_contextid(),
313 'component' => 'contentbank',
314 'filearea' => 'public',
315 'itemid' => $this->get_id(),
317 'filename' => $file->get_filename(),
318 'timecreated' => time(),
320 return $fs->create_file_from_storedfile($filerecord, $file);
325 * Returns the $file related to this content.
327 * @return stored_file File stored in content bank area related to the given itemid.
328 * @throws \coding_exception if not loaded.
330 public function get_file(): ?stored_file
{
331 $itemid = $this->get_id();
332 $fs = get_file_storage();
333 $files = $fs->get_area_files(
334 $this->content
->contextid
,
338 'itemid, filepath, filename',
341 if (!empty($files)) {
342 $file = reset($files);
349 * Returns the places where the file associated to this content is used or an empty array if the content has no file.
351 * @return array of stored_file where current file content is used or empty array if it hasn't any file.
354 public function get_uses(): ?
array {
357 $file = $this->get_file();
359 $fs = get_file_storage();
360 $references = $fs->get_references_by_storedfile($file);
367 * Returns the file url related to this content.
369 * @return string URL of the file stored in content bank area related to the given itemid.
370 * @throws \coding_exception if not loaded.
372 public function get_file_url(): string {
373 if (!$file = $this->get_file()) {
376 $fileurl = moodle_url
::make_pluginfile_url(
377 $this->content
->contextid
,
381 $file->get_filepath(),
382 $file->get_filename()
389 * Returns user has access permission for the content itself (based on what plugin needs).
391 * @return bool True if content could be accessed. False otherwise.
393 public function is_view_allowed(): bool {
394 // Plugins can overwrite this method in case they want to check something related to content properties.
396 $context = \context
::instance_by_id($this->get_contextid());
398 return $USER->id
== $this->content
->usercreated ||
399 $this->get_visibility() == self
::VISIBILITY_PUBLIC ||
400 has_capability('moodle/contentbank:viewunlistedcontent', $context);