Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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