Differences Between: [Versions 310 and 402] [Versions 39 and 402] [Versions 402 and 403]
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 /** 18 * Content 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 */ 24 25 namespace core_contentbank; 26 27 use core_text; 28 use stored_file; 29 use stdClass; 30 use coding_exception; 31 use context; 32 use moodle_url; 33 use core\event\contentbank_content_updated; 34 35 /** 36 * Content manager class 37 * 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 41 */ 42 abstract class content { 43 /** 44 * @var int Visibility value. Public content is visible to all users with access to the content bank of the 45 * appropriate context. 46 */ 47 public const VISIBILITY_PUBLIC = 1; 48 49 /** 50 * @var int Visibility value. Unlisted content is only visible to the author and to users with 51 * moodle/contentbank:viewunlistedcontent capability. 52 */ 53 public const VISIBILITY_UNLISTED = 2; 54 55 /** @var stdClass $content The content of the current instance. **/ 56 protected $content = null; 57 58 /** 59 * Content bank constructor 60 * 61 * @param stdClass $record A contentbank_content record. 62 * @throws coding_exception If content type is not right. 63 */ 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)); 69 } 70 $typeclass = $record->contenttype.'\\contenttype'; 71 if (!class_exists($typeclass)) { 72 throw new coding_exception(get_string('contenttypenotfound', 'error', $record->contenttype)); 73 } 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')); 78 } 79 $this->content = $record; 80 } 81 82 /** 83 * Returns $this->content. 84 * 85 * @return stdClass $this->content. 86 */ 87 public function get_content(): stdClass { 88 return $this->content; 89 } 90 91 /** 92 * Returns $this->content->contenttype. 93 * 94 * @return string $this->content->contenttype. 95 */ 96 public function get_content_type(): string { 97 return $this->content->contenttype; 98 } 99 100 /** 101 * Return the contenttype instance of this content. 102 * 103 * @return contenttype The content type instance 104 */ 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); 109 } 110 111 /** 112 * Returns $this->content->timemodified. 113 * 114 * @return int $this->content->timemodified. 115 */ 116 public function get_timemodified(): int { 117 return $this->content->timemodified; 118 } 119 120 /** 121 * Updates content_bank table with information in $this->content. 122 * 123 * @return boolean True if the content has been succesfully updated. False otherwise. 124 * @throws \coding_exception if not loaded. 125 */ 126 public function update_content(): bool { 127 global $USER, $DB; 128 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')); 133 } 134 $this->content->usermodified = $USER->id; 135 $this->content->timemodified = time(); 136 $result = $DB->update_record('contentbank_content', $this->content); 137 if ($result) { 138 // Trigger an event for updating this content. 139 $event = contentbank_content_updated::create_from_record($this->content); 140 $event->trigger(); 141 } 142 return $result; 143 } 144 145 /** 146 * Set a new name to the content. 147 * 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. 151 */ 152 public function set_name(string $name): bool { 153 $name = trim($name); 154 if ($name === '') { 155 return false; 156 } 157 158 // Clean name. 159 $name = clean_param($name, PARAM_TEXT); 160 if (core_text::strlen($name) > 255) { 161 $name = core_text::substr($name, 0, 255); 162 } 163 164 $oldname = $this->content->name; 165 $this->content->name = $name; 166 $updated = $this->update_content(); 167 if (!$updated) { 168 $this->content->name = $oldname; 169 } 170 return $updated; 171 } 172 173 /** 174 * Returns the name of the content. 175 * 176 * @return string The name of the content. 177 */ 178 public function get_name(): string { 179 return $this->content->name; 180 } 181 182 /** 183 * Set a new contextid to the content. 184 * 185 * @param int $contextid The new contextid of the content. 186 * @return bool True if the content has been succesfully updated. False otherwise. 187 */ 188 public function set_contextid(int $contextid): bool { 189 if ($this->content->contextid == $contextid) { 190 return true; 191 } 192 193 $oldcontextid = $this->content->contextid; 194 $this->content->contextid = $contextid; 195 $updated = $this->update_content(); 196 if ($updated) { 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); 200 } else { 201 $this->content->contextid = $oldcontextid; 202 } 203 return $updated; 204 } 205 206 /** 207 * Returns the contextid of the content. 208 * 209 * @return int The id of the content context. 210 */ 211 public function get_contextid(): string { 212 return $this->content->contextid; 213 } 214 215 /** 216 * Returns the content ID. 217 * 218 * @return int The content ID. 219 */ 220 public function get_id(): int { 221 return $this->content->id; 222 } 223 224 /** 225 * Change the content instanceid value. 226 * 227 * @param int $instanceid New instanceid for this content 228 * @return boolean True if the instanceid has been succesfully updated. False otherwise. 229 */ 230 public function set_instanceid(int $instanceid): bool { 231 $this->content->instanceid = $instanceid; 232 return $this->update_content(); 233 } 234 235 /** 236 * Returns the $instanceid of this content. 237 * 238 * @return int contentbank instanceid 239 */ 240 public function get_instanceid(): int { 241 return $this->content->instanceid; 242 } 243 244 /** 245 * Change the content config values. 246 * 247 * @param string $configdata New config information for this content 248 * @return boolean True if the configdata has been succesfully updated. False otherwise. 249 */ 250 public function set_configdata(string $configdata): bool { 251 $this->content->configdata = $configdata; 252 return $this->update_content(); 253 } 254 255 /** 256 * Return the content config values. 257 * 258 * @return mixed Config information for this content (json decoded) 259 */ 260 public function get_configdata() { 261 return $this->content->configdata; 262 } 263 264 /** 265 * Sets a new content visibility and saves it to database. 266 * 267 * @param int $visibility Must be self::PUBLIC or self::UNLISTED 268 * @return bool 269 * @throws coding_exception 270 */ 271 public function set_visibility(int $visibility): bool { 272 if (!in_array($visibility, [self::VISIBILITY_PUBLIC, self::VISIBILITY_UNLISTED])) { 273 return false; 274 } 275 $this->content->visibility = $visibility; 276 return $this->update_content(); 277 } 278 279 /** 280 * Return true if the content may be shown to other users in the content bank. 281 * 282 * @return boolean 283 */ 284 public function get_visibility(): int { 285 return $this->content->visibility; 286 } 287 288 /** 289 * Import a file as a valid content. 290 * 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. 294 * 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 297 * and also parse it. 298 * 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. 302 */ 303 public function import_file(stored_file $file): ?stored_file { 304 $originalfile = $this->get_file(); 305 if ($originalfile) { 306 $originalfile->replace_file_with($file); 307 return $originalfile; 308 } else { 309 $itemid = $this->get_id(); 310 $fs = get_file_storage(); 311 $filerecord = [ 312 'contextid' => $this->get_contextid(), 313 'component' => 'contentbank', 314 'filearea' => 'public', 315 'itemid' => $this->get_id(), 316 'filepath' => '/', 317 'filename' => $file->get_filename(), 318 'timecreated' => time(), 319 ]; 320 return $fs->create_file_from_storedfile($filerecord, $file); 321 } 322 } 323 324 /** 325 * Returns the $file related to this content. 326 * 327 * @return stored_file File stored in content bank area related to the given itemid. 328 * @throws \coding_exception if not loaded. 329 */ 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, 335 'contentbank', 336 'public', 337 $itemid, 338 'itemid, filepath, filename', 339 false 340 ); 341 if (!empty($files)) { 342 $file = reset($files); 343 return $file; 344 } 345 return null; 346 } 347 348 /** 349 * Returns the places where the file associated to this content is used or an empty array if the content has no file. 350 * 351 * @return array of stored_file where current file content is used or empty array if it hasn't any file. 352 * @since 3.11 353 */ 354 public function get_uses(): ?array { 355 $references = []; 356 357 $file = $this->get_file(); 358 if ($file != null) { 359 $fs = get_file_storage(); 360 $references = $fs->get_references_by_storedfile($file); 361 } 362 363 return $references; 364 } 365 366 /** 367 * Returns the file url related to this content. 368 * 369 * @return string URL of the file stored in content bank area related to the given itemid. 370 * @throws \coding_exception if not loaded. 371 */ 372 public function get_file_url(): string { 373 if (!$file = $this->get_file()) { 374 return ''; 375 } 376 $fileurl = moodle_url::make_pluginfile_url( 377 $this->content->contextid, 378 'contentbank', 379 'public', 380 $file->get_itemid(), 381 $file->get_filepath(), 382 $file->get_filename() 383 ); 384 385 return $fileurl; 386 } 387 388 /** 389 * Returns user has access permission for the content itself (based on what plugin needs). 390 * 391 * @return bool True if content could be accessed. False otherwise. 392 */ 393 public function is_view_allowed(): bool { 394 // Plugins can overwrite this method in case they want to check something related to content properties. 395 global $USER; 396 $context = \context::instance_by_id($this->get_contextid()); 397 398 return $USER->id == $this->content->usercreated || 399 $this->get_visibility() == self::VISIBILITY_PUBLIC || 400 has_capability('moodle/contentbank:viewunlistedcontent', $context); 401 } 402 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body