Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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 stdClass $content The content of the current instance. **/
  45      protected $content  = null;
  46  
  47      /**
  48       * Content bank constructor
  49       *
  50       * @param stdClass $record A contentbank_content record.
  51       * @throws coding_exception If content type is not right.
  52       */
  53      public function __construct(stdClass $record) {
  54          // Content type should exist and be linked to plugin classname.
  55          $classname = $record->contenttype.'\\content';
  56          if (get_class($this) != $classname) {
  57              throw new coding_exception(get_string('contenttypenotfound', 'error', $record->contenttype));
  58          }
  59          $typeclass = $record->contenttype.'\\contenttype';
  60          if (!class_exists($typeclass)) {
  61              throw new coding_exception(get_string('contenttypenotfound', 'error', $record->contenttype));
  62          }
  63          // A record with the id must exist in 'contentbank_content' table.
  64          // To improve performance, we are only checking the id is set, but no querying the database.
  65          if (!isset($record->id)) {
  66              throw new coding_exception(get_string('invalidcontentid', 'error'));
  67          }
  68          $this->content = $record;
  69      }
  70  
  71      /**
  72       * Returns $this->content.
  73       *
  74       * @return stdClass  $this->content.
  75       */
  76      public function get_content(): stdClass {
  77          return $this->content;
  78      }
  79  
  80      /**
  81       * Returns $this->content->contenttype.
  82       *
  83       * @return string  $this->content->contenttype.
  84       */
  85      public function get_content_type(): string {
  86          return $this->content->contenttype;
  87      }
  88  
  89      /**
  90       * Return the contenttype instance of this content.
  91       *
  92       * @return contenttype The content type instance
  93       */
  94      public function get_content_type_instance(): contenttype {
  95          $context = context::instance_by_id($this->content->contextid);
  96          $contenttypeclass = "\\{$this->content->contenttype}\\contenttype";
  97          return new $contenttypeclass($context);
  98      }
  99  
 100      /**
 101       * Returns $this->content->timemodified.
 102       *
 103       * @return int  $this->content->timemodified.
 104       */
 105      public function get_timemodified(): int {
 106          return $this->content->timemodified;
 107      }
 108  
 109      /**
 110       * Updates content_bank table with information in $this->content.
 111       *
 112       * @return boolean  True if the content has been succesfully updated. False otherwise.
 113       * @throws \coding_exception if not loaded.
 114       */
 115      public function update_content(): bool {
 116          global $USER, $DB;
 117  
 118          // A record with the id must exist in 'contentbank_content' table.
 119          // To improve performance, we are only checking the id is set, but no querying the database.
 120          if (!isset($this->content->id)) {
 121              throw new coding_exception(get_string('invalidcontentid', 'error'));
 122          }
 123          $this->content->usermodified = $USER->id;
 124          $this->content->timemodified = time();
 125          $result = $DB->update_record('contentbank_content', $this->content);
 126          if ($result) {
 127              // Trigger an event for updating this content.
 128              $event = contentbank_content_updated::create_from_record($this->content);
 129              $event->trigger();
 130          }
 131          return $result;
 132      }
 133  
 134      /**
 135       * Set a new name to the content.
 136       *
 137       * @param string $name  The name of the content.
 138       * @return bool  True if the content has been succesfully updated. False otherwise.
 139       * @throws \coding_exception if not loaded.
 140       */
 141      public function set_name(string $name): bool {
 142          $name = trim($name);
 143          if ($name === '') {
 144              return false;
 145          }
 146  
 147          // Clean name.
 148          $name = clean_param($name, PARAM_TEXT);
 149          if (core_text::strlen($name) > 255) {
 150              $name = core_text::substr($name, 0, 255);
 151          }
 152  
 153          $oldname = $this->content->name;
 154          $this->content->name = $name;
 155          $updated = $this->update_content();
 156          if (!$updated) {
 157              $this->content->name = $oldname;
 158          }
 159          return $updated;
 160      }
 161  
 162      /**
 163       * Returns the name of the content.
 164       *
 165       * @return string   The name of the content.
 166       */
 167      public function get_name(): string {
 168          return $this->content->name;
 169      }
 170  
 171      /**
 172       * Set a new contextid to the content.
 173       *
 174       * @param int $contextid  The new contextid of the content.
 175       * @return bool  True if the content has been succesfully updated. False otherwise.
 176       */
 177      public function set_contextid(int $contextid): bool {
 178          if ($this->content->contextid == $contextid) {
 179              return true;
 180          }
 181  
 182          $oldcontextid = $this->content->contextid;
 183          $this->content->contextid = $contextid;
 184          $updated = $this->update_content();
 185          if ($updated) {
 186              // Move files to new context
 187              $fs = get_file_storage();
 188              $fs->move_area_files_to_new_context($oldcontextid, $contextid, 'contentbank', 'public', $this->content->id);
 189          } else {
 190              $this->content->contextid = $oldcontextid;
 191          }
 192          return $updated;
 193      }
 194  
 195      /**
 196       * Returns the contextid of the content.
 197       *
 198       * @return int   The id of the content context.
 199       */
 200      public function get_contextid(): string {
 201          return $this->content->contextid;
 202      }
 203  
 204      /**
 205       * Returns the content ID.
 206       *
 207       * @return int   The content ID.
 208       */
 209      public function get_id(): int {
 210          return $this->content->id;
 211      }
 212  
 213      /**
 214       * Change the content instanceid value.
 215       *
 216       * @param int $instanceid    New instanceid for this content
 217       * @return boolean           True if the instanceid has been succesfully updated. False otherwise.
 218       */
 219      public function set_instanceid(int $instanceid): bool {
 220          $this->content->instanceid = $instanceid;
 221          return $this->update_content();
 222      }
 223  
 224      /**
 225       * Returns the $instanceid of this content.
 226       *
 227       * @return int   contentbank instanceid
 228       */
 229      public function get_instanceid(): int {
 230          return $this->content->instanceid;
 231      }
 232  
 233      /**
 234       * Change the content config values.
 235       *
 236       * @param string $configdata    New config information for this content
 237       * @return boolean              True if the configdata has been succesfully updated. False otherwise.
 238       */
 239      public function set_configdata(string $configdata): bool {
 240          $this->content->configdata = $configdata;
 241          return $this->update_content();
 242      }
 243  
 244      /**
 245       * Return the content config values.
 246       *
 247       * @return mixed   Config information for this content (json decoded)
 248       */
 249      public function get_configdata() {
 250          return $this->content->configdata;
 251      }
 252  
 253      /**
 254       * Import a file as a valid content.
 255       *
 256       * By default, all content has a public file area to interact with the content bank
 257       * repository. This method should be overridden by contentypes which does not simply
 258       * upload to the public file area.
 259       *
 260       * If any, the method will return the final stored_file. This way it can be invoked
 261       * as parent::import_file in case any plugin want to store the file in the public area
 262       * and also parse it.
 263       *
 264       * @throws file_exception If file operations fail
 265       * @param stored_file $file File to store in the content file area.
 266       * @return stored_file|null the stored content file or null if the file is discarted.
 267       */
 268      public function import_file(stored_file $file): ?stored_file {
 269          $originalfile = $this->get_file();
 270          if ($originalfile) {
 271              $originalfile->replace_file_with($file);
 272              return $originalfile;
 273          } else {
 274              $itemid = $this->get_id();
 275              $fs = get_file_storage();
 276              $filerecord = [
 277                  'contextid' => $this->get_contextid(),
 278                  'component' => 'contentbank',
 279                  'filearea' => 'public',
 280                  'itemid' => $this->get_id(),
 281                  'filepath' => '/',
 282                  'filename' => $file->get_filename(),
 283                  'timecreated' => time(),
 284              ];
 285              return $fs->create_file_from_storedfile($filerecord, $file);
 286          }
 287      }
 288  
 289      /**
 290       * Returns the $file related to this content.
 291       *
 292       * @return stored_file  File stored in content bank area related to the given itemid.
 293       * @throws \coding_exception if not loaded.
 294       */
 295      public function get_file(): ?stored_file {
 296          $itemid = $this->get_id();
 297          $fs = get_file_storage();
 298          $files = $fs->get_area_files(
 299              $this->content->contextid,
 300              'contentbank',
 301              'public',
 302              $itemid,
 303              'itemid, filepath, filename',
 304              false
 305          );
 306          if (!empty($files)) {
 307              $file = reset($files);
 308              return $file;
 309          }
 310          return null;
 311      }
 312  
 313      /**
 314       * Returns the file url related to this content.
 315       *
 316       * @return string       URL of the file stored in content bank area related to the given itemid.
 317       * @throws \coding_exception if not loaded.
 318       */
 319      public function get_file_url(): string {
 320          if (!$file = $this->get_file()) {
 321              return '';
 322          }
 323          $fileurl = moodle_url::make_pluginfile_url(
 324              $this->content->contextid,
 325              'contentbank',
 326              'public',
 327              $file->get_itemid(),
 328              $file->get_filepath(),
 329              $file->get_filename()
 330          );
 331  
 332          return $fileurl;
 333      }
 334  
 335      /**
 336       * Returns user has access permission for the content itself (based on what plugin needs).
 337       *
 338       * @return bool     True if content could be accessed. False otherwise.
 339       */
 340      public function is_view_allowed(): bool {
 341          // There's no capability at content level to check,
 342          // but plugins can overwrite this method in case they want to check something related to content properties.
 343          return true;
 344      }
 345  }