Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * Private resource module utility functions
  20   *
  21   * @package    mod_resource
  22   * @copyright  2009 Petr Skoda  {@link http://skodak.org}
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die;
  27  
  28  require_once("$CFG->libdir/filelib.php");
  29  require_once("$CFG->libdir/resourcelib.php");
  30  require_once("$CFG->dirroot/mod/resource/lib.php");
  31  
  32  /**
  33   * Redirected to migrated resource if needed,
  34   * return if incorrect parameters specified
  35   * @param int $oldid
  36   * @param int $cmid
  37   * @return void
  38   */
  39  function resource_redirect_if_migrated($oldid, $cmid) {
  40      global $DB, $CFG;
  41  
  42      if ($oldid) {
  43          $old = $DB->get_record('resource_old', array('oldid'=>$oldid));
  44      } else {
  45          $old = $DB->get_record('resource_old', array('cmid'=>$cmid));
  46      }
  47  
  48      if (!$old) {
  49          return;
  50      }
  51  
  52      redirect("$CFG->wwwroot/mod/$old->newmodule/view.php?id=".$old->cmid);
  53  }
  54  
  55  /**
  56   * Display embedded resource file.
  57   * @param object $resource
  58   * @param object $cm
  59   * @param object $course
  60   * @param stored_file $file main file
  61   * @return does not return
  62   */
  63  function resource_display_embed($resource, $cm, $course, $file) {
  64      global $PAGE, $OUTPUT, $USER;
  65  
  66      $clicktoopen = resource_get_clicktoopen($file, $resource->revision);
  67  
  68      $context = context_module::instance($cm->id);
  69      $moodleurl = moodle_url::make_pluginfile_url($context->id, 'mod_resource', 'content', $resource->revision,
  70              $file->get_filepath(), $file->get_filename());
  71  
  72      $mimetype = $file->get_mimetype();
  73      $title    = $resource->name;
  74  
  75      $extension = resourcelib_get_extension($file->get_filename());
  76  
  77      $mediamanager = core_media_manager::instance($PAGE);
  78      $embedoptions = array(
  79          core_media_manager::OPTION_TRUSTED => true,
  80          core_media_manager::OPTION_BLOCK => true,
  81      );
  82  
  83      if (file_mimetype_in_typegroup($mimetype, 'web_image')) {  // It's an image
  84          $code = resourcelib_embed_image($moodleurl->out(), $title);
  85  
  86      } else if ($mimetype === 'application/pdf') {
  87          // PDF document
  88          $code = resourcelib_embed_pdf($moodleurl->out(), $title, $clicktoopen);
  89  
  90      } else if ($mediamanager->can_embed_url($moodleurl, $embedoptions)) {
  91          // Media (audio/video) file.
  92          $code = $mediamanager->embed_url($moodleurl, $title, 0, 0, $embedoptions);
  93  
  94      } else {
  95          // We need a way to discover if we are loading remote docs inside an iframe.
  96          $moodleurl->param('embed', 1);
  97  
  98          // anything else - just try object tag enlarged as much as possible
  99          $code = resourcelib_embed_general($moodleurl, $title, $clicktoopen, $mimetype);
 100      }
 101  
 102      resource_print_header($resource, $cm, $course);
 103      resource_print_heading($resource, $cm, $course);
 104  
 105      // Display any activity information (eg completion requirements / dates).
 106      $cminfo = cm_info::create($cm);
 107      $completiondetails = \core_completion\cm_completion_details::get_instance($cminfo, $USER->id);
 108      $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id);
 109      echo $OUTPUT->activity_information($cminfo, $completiondetails, $activitydates);
 110  
 111      echo format_text($code, FORMAT_HTML, ['noclean' => true]);
 112  
 113      resource_print_intro($resource, $cm, $course);
 114  
 115      echo $OUTPUT->footer();
 116      die;
 117  }
 118  
 119  /**
 120   * Display resource frames.
 121   * @param object $resource
 122   * @param object $cm
 123   * @param object $course
 124   * @param stored_file $file main file
 125   * @return does not return
 126   */
 127  function resource_display_frame($resource, $cm, $course, $file) {
 128      global $PAGE, $OUTPUT, $CFG;
 129  
 130      $frame = optional_param('frameset', 'main', PARAM_ALPHA);
 131  
 132      if ($frame === 'top') {
 133          $PAGE->set_pagelayout('frametop');
 134          resource_print_header($resource, $cm, $course);
 135          resource_print_heading($resource, $cm, $course);
 136          resource_print_intro($resource, $cm, $course);
 137          echo $OUTPUT->footer();
 138          die;
 139  
 140      } else {
 141          $config = get_config('resource');
 142          $context = context_module::instance($cm->id);
 143          $path = '/'.$context->id.'/mod_resource/content/'.$resource->revision.$file->get_filepath().$file->get_filename();
 144          $fileurl = file_encode_url($CFG->wwwroot.'/pluginfile.php', $path, false);
 145          $navurl = "$CFG->wwwroot/mod/resource/view.php?id=$cm->id&amp;frameset=top";
 146          $title = strip_tags(format_string($course->shortname.': '.$resource->name));
 147          $framesize = $config->framesize;
 148          $contentframetitle = s(format_string($resource->name));
 149          $modulename = s(get_string('modulename','resource'));
 150          $dir = get_string('thisdirection', 'langconfig');
 151  
 152          $file = <<<EOF
 153  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
 154  <html dir="$dir">
 155    <head>
 156      <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 157      <title>$title</title>
 158    </head>
 159    <frameset rows="$framesize,*">
 160      <frame src="$navurl" title="$modulename" />
 161      <frame src="$fileurl" title="$contentframetitle" />
 162    </frameset>
 163  </html>
 164  EOF;
 165  
 166          @header('Content-Type: text/html; charset=utf-8');
 167          echo $file;
 168          die;
 169      }
 170  }
 171  
 172  /**
 173   * Internal function - create click to open text with link.
 174   */
 175  function resource_get_clicktoopen($file, $revision, $extra='') {
 176      global $CFG;
 177  
 178      $filename = $file->get_filename();
 179      $path = '/'.$file->get_contextid().'/mod_resource/content/'.$revision.$file->get_filepath().$file->get_filename();
 180      $fullurl = file_encode_url($CFG->wwwroot.'/pluginfile.php', $path, false);
 181  
 182      $string = get_string('clicktoopen2', 'resource', "<a href=\"$fullurl\" $extra>$filename</a>");
 183  
 184      return $string;
 185  }
 186  
 187  /**
 188   * Internal function - create click to open text with link.
 189   */
 190  function resource_get_clicktodownload($file, $revision) {
 191      global $CFG;
 192  
 193      $filename = $file->get_filename();
 194      $path = '/'.$file->get_contextid().'/mod_resource/content/'.$revision.$file->get_filepath().$file->get_filename();
 195      $fullurl = file_encode_url($CFG->wwwroot.'/pluginfile.php', $path, true);
 196  
 197      $string = get_string('clicktodownload', 'resource', "<a href=\"$fullurl\">$filename</a>");
 198  
 199      return $string;
 200  }
 201  
 202  /**
 203   * Print resource info and workaround link when JS not available.
 204   * @param object $resource
 205   * @param object $cm
 206   * @param object $course
 207   * @param stored_file $file main file
 208   * @return does not return
 209   */
 210  function resource_print_workaround($resource, $cm, $course, $file) {
 211      global $CFG, $OUTPUT, $USER;
 212      resource_print_header($resource, $cm, $course);
 213      resource_print_heading($resource, $cm, $course, true);
 214  
 215      // Display any activity information (eg completion requirements / dates).
 216      $cminfo = cm_info::create($cm);
 217      $completiondetails = \core_completion\cm_completion_details::get_instance($cminfo, $USER->id);
 218      $activitydates = \core\activity_dates::get_dates_for_module($cminfo, $USER->id);
 219      echo $OUTPUT->activity_information($cminfo, $completiondetails, $activitydates);
 220  
 221      resource_print_intro($resource, $cm, $course, true);
 222  
 223      $resource->mainfile = $file->get_filename();
 224      echo '<div class="resourceworkaround">';
 225      switch (resource_get_final_display_type($resource)) {
 226          case RESOURCELIB_DISPLAY_POPUP:
 227              $path = '/'.$file->get_contextid().'/mod_resource/content/'.$resource->revision.$file->get_filepath().$file->get_filename();
 228              $fullurl = file_encode_url($CFG->wwwroot.'/pluginfile.php', $path, false);
 229              $options = empty($resource->displayoptions) ? [] : (array) unserialize_array($resource->displayoptions);
 230              $width  = empty($options['popupwidth'])  ? 620 : $options['popupwidth'];
 231              $height = empty($options['popupheight']) ? 450 : $options['popupheight'];
 232              $wh = "width=$width,height=$height,toolbar=no,location=no,menubar=no,copyhistory=no,status=no,directories=no,scrollbars=yes,resizable=yes";
 233              $extra = "onclick=\"window.open('$fullurl', '', '$wh'); return false;\"";
 234              echo resource_get_clicktoopen($file, $resource->revision, $extra);
 235              break;
 236  
 237          case RESOURCELIB_DISPLAY_NEW:
 238              $extra = 'onclick="this.target=\'_blank\'"';
 239              echo resource_get_clicktoopen($file, $resource->revision, $extra);
 240              break;
 241  
 242          case RESOURCELIB_DISPLAY_DOWNLOAD:
 243              echo resource_get_clicktodownload($file, $resource->revision);
 244              break;
 245  
 246          case RESOURCELIB_DISPLAY_OPEN:
 247          default:
 248              echo resource_get_clicktoopen($file, $resource->revision);
 249              break;
 250      }
 251      echo '</div>';
 252  
 253      echo $OUTPUT->footer();
 254      die;
 255  }
 256  
 257  /**
 258   * Print resource header.
 259   * @param object $resource
 260   * @param object $cm
 261   * @param object $course
 262   * @return void
 263   */
 264  function resource_print_header($resource, $cm, $course) {
 265      global $PAGE, $OUTPUT;
 266  
 267      $PAGE->set_title($course->shortname.': '.$resource->name);
 268      $PAGE->set_heading($course->fullname);
 269      $PAGE->set_activity_record($resource);
 270      echo $OUTPUT->header();
 271  }
 272  
 273  /**
 274   * Print resource heading.
 275   * @param object $resource
 276   * @param object $cm
 277   * @param object $course
 278   * @param bool $notused This variable is no longer used
 279   * @return void
 280   */
 281  function resource_print_heading($resource, $cm, $course, $notused = false) {
 282      global $OUTPUT;
 283      echo $OUTPUT->heading(format_string($resource->name), 2);
 284  }
 285  
 286  
 287  /**
 288   * Gets details of the file to cache in course cache to be displayed using {@link resource_get_optional_details()}
 289   *
 290   * @param object $resource Resource table row (only property 'displayoptions' is used here)
 291   * @param object $cm Course-module table row
 292   * @return string Size and type or empty string if show options are not enabled
 293   */
 294  function resource_get_file_details($resource, $cm) {
 295      $options = empty($resource->displayoptions) ? [] : (array) unserialize_array($resource->displayoptions);
 296      $filedetails = array();
 297      if (!empty($options['showsize']) || !empty($options['showtype']) || !empty($options['showdate'])) {
 298          $context = context_module::instance($cm->id);
 299          $fs = get_file_storage();
 300          $files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder DESC, id ASC', false);
 301          // For a typical file resource, the sortorder is 1 for the main file
 302          // and 0 for all other files. This sort approach is used just in case
 303          // there are situations where the file has a different sort order.
 304          $mainfile = $files ? reset($files) : null;
 305          if (!empty($options['showsize'])) {
 306              $filedetails['size'] = 0;
 307              foreach ($files as $file) {
 308                  // This will also synchronize the file size for external files if needed.
 309                  $filedetails['size'] += $file->get_filesize();
 310                  if ($file->get_repository_id()) {
 311                      // If file is a reference the 'size' attribute can not be cached.
 312                      $filedetails['isref'] = true;
 313                  }
 314              }
 315          }
 316          if (!empty($options['showtype'])) {
 317              if ($mainfile) {
 318                  $filedetails['type'] = get_mimetype_description($mainfile);
 319                  $filedetails['mimetype'] = $mainfile->get_mimetype();
 320                  // Only show type if it is not unknown.
 321                  if ($filedetails['type'] === get_mimetype_description('document/unknown')) {
 322                      $filedetails['type'] = '';
 323                  }
 324              } else {
 325                  $filedetails['type'] = '';
 326              }
 327          }
 328          if (!empty($options['showdate'])) {
 329              if ($mainfile) {
 330                  // Modified date may be up to several minutes later than uploaded date just because
 331                  // teacher did not submit the form promptly. Give teacher up to 5 minutes to do it.
 332                  if ($mainfile->get_timemodified() > $mainfile->get_timecreated() + 5 * MINSECS) {
 333                      $filedetails['modifieddate'] = $mainfile->get_timemodified();
 334                  } else {
 335                      $filedetails['uploadeddate'] = $mainfile->get_timecreated();
 336                  }
 337                  if ($mainfile->get_repository_id()) {
 338                      // If main file is a reference the 'date' attribute can not be cached.
 339                      $filedetails['isref'] = true;
 340                  }
 341              } else {
 342                  $filedetails['uploadeddate'] = '';
 343              }
 344          }
 345      }
 346      return $filedetails;
 347  }
 348  
 349  /**
 350   * Gets optional details for a resource, depending on resource settings.
 351   *
 352   * Result may include the file size and type if those settings are chosen,
 353   * or blank if none.
 354   *
 355   * @param object $resource Resource table row (only property 'displayoptions' is used here)
 356   * @param object $cm Course-module table row
 357   * @return string Size and type or empty string if show options are not enabled
 358   */
 359  function resource_get_optional_details($resource, $cm) {
 360      global $DB;
 361  
 362      $details = '';
 363  
 364      $options = empty($resource->displayoptions) ? [] : (array) unserialize_array($resource->displayoptions);
 365      if (!empty($options['showsize']) || !empty($options['showtype']) || !empty($options['showdate'])) {
 366          if (!array_key_exists('filedetails', $options)) {
 367              $filedetails = resource_get_file_details($resource, $cm);
 368          } else {
 369              $filedetails = $options['filedetails'];
 370          }
 371          $size = '';
 372          $type = '';
 373          $date = '';
 374          $langstring = '';
 375          $infodisplayed = 0;
 376          if (!empty($options['showsize'])) {
 377              if (!empty($filedetails['size'])) {
 378                  $size = display_size($filedetails['size']);
 379                  $langstring .= 'size';
 380                  $infodisplayed += 1;
 381              }
 382          }
 383          if (!empty($options['showtype'])) {
 384              if (!empty($filedetails['type'])) {
 385                  $type = $filedetails['type'];
 386                  $langstring .= 'type';
 387                  $infodisplayed += 1;
 388              }
 389          }
 390          if (!empty($options['showdate']) && (!empty($filedetails['modifieddate']) || !empty($filedetails['uploadeddate']))) {
 391              if (!empty($filedetails['modifieddate'])) {
 392                  $date = get_string('modifieddate', 'mod_resource', userdate($filedetails['modifieddate'],
 393                      get_string('strftimedatetimeshort', 'langconfig')));
 394              } else if (!empty($filedetails['uploadeddate'])) {
 395                  $date = get_string('uploadeddate', 'mod_resource', userdate($filedetails['uploadeddate'],
 396                      get_string('strftimedatetimeshort', 'langconfig')));
 397              }
 398              $langstring .= 'date';
 399              $infodisplayed += 1;
 400          }
 401  
 402          if ($infodisplayed > 1) {
 403              $details = get_string("resourcedetails_{$langstring}", 'resource',
 404                      (object)array('size' => $size, 'type' => $type, 'date' => $date));
 405          } else {
 406              // Only one of size, type and date is set, so just append.
 407              $details = $size . $type . $date;
 408          }
 409      }
 410  
 411      return $details;
 412  }
 413  
 414  /**
 415   * Print resource introduction.
 416   * @param object $resource
 417   * @param object $cm
 418   * @param object $course
 419   * @param bool $ignoresettings print even if not specified in modedit
 420   * @return void
 421   */
 422  function resource_print_intro($resource, $cm, $course, $ignoresettings=false) {
 423      global $OUTPUT;
 424  
 425      $options = empty($resource->displayoptions) ? [] : (array) unserialize_array($resource->displayoptions);
 426  
 427      $extraintro = resource_get_optional_details($resource, $cm);
 428      if ($extraintro) {
 429          // Put a paragaph tag around the details
 430          $extraintro = html_writer::tag('p', $extraintro, array('class' => 'resourcedetails'));
 431      }
 432  
 433      if ($ignoresettings || !empty($options['printintro']) || $extraintro) {
 434          $gotintro = !html_is_blank($resource->intro);
 435          if ($gotintro || $extraintro) {
 436              echo $OUTPUT->box_start('mod_introbox', 'resourceintro');
 437              if ($gotintro) {
 438                  echo format_module_intro('resource', $resource, $cm->id);
 439              }
 440              echo $extraintro;
 441              echo $OUTPUT->box_end();
 442          }
 443      }
 444  }
 445  
 446  /**
 447   * Print warning that instance not migrated yet.
 448   * @param object $resource
 449   * @param object $cm
 450   * @param object $course
 451   * @return void, does not return
 452   */
 453  function resource_print_tobemigrated($resource, $cm, $course) {
 454      global $DB, $OUTPUT;
 455  
 456      $resource_old = $DB->get_record('resource_old', array('oldid'=>$resource->id));
 457      resource_print_header($resource, $cm, $course);
 458      resource_print_heading($resource, $cm, $course);
 459      resource_print_intro($resource, $cm, $course);
 460      echo $OUTPUT->notification(get_string('notmigrated', 'resource', $resource_old->type));
 461      echo $OUTPUT->footer();
 462      die;
 463  }
 464  
 465  /**
 466   * Print warning that file can not be found.
 467   * @param object $resource
 468   * @param object $cm
 469   * @param object $course
 470   * @return void, does not return
 471   */
 472  function resource_print_filenotfound($resource, $cm, $course) {
 473      global $DB, $OUTPUT;
 474  
 475      $resource_old = $DB->get_record('resource_old', array('oldid'=>$resource->id));
 476      resource_print_header($resource, $cm, $course);
 477      resource_print_heading($resource, $cm, $course);
 478      resource_print_intro($resource, $cm, $course);
 479      if ($resource_old) {
 480          echo $OUTPUT->notification(get_string('notmigrated', 'resource', $resource_old->type));
 481      } else {
 482          echo $OUTPUT->notification(get_string('filenotfound', 'resource'));
 483      }
 484      echo $OUTPUT->footer();
 485      die;
 486  }
 487  
 488  /**
 489   * Decide the best display format.
 490   * @param object $resource
 491   * @return int display type constant
 492   */
 493  function resource_get_final_display_type($resource) {
 494      global $CFG, $PAGE;
 495  
 496      if ($resource->display != RESOURCELIB_DISPLAY_AUTO) {
 497          return $resource->display;
 498      }
 499  
 500      if (empty($resource->mainfile)) {
 501          return RESOURCELIB_DISPLAY_DOWNLOAD;
 502      } else {
 503          $mimetype = mimeinfo('type', $resource->mainfile);
 504      }
 505  
 506      if (file_mimetype_in_typegroup($mimetype, 'archive')) {
 507          return RESOURCELIB_DISPLAY_DOWNLOAD;
 508      }
 509      if (file_mimetype_in_typegroup($mimetype, array('web_image', '.htm', 'web_video', 'web_audio'))) {
 510          return RESOURCELIB_DISPLAY_EMBED;
 511      }
 512  
 513      // let the browser deal with it somehow
 514      return RESOURCELIB_DISPLAY_OPEN;
 515  }
 516  
 517  /**
 518   * File browsing support class
 519   */
 520  class resource_content_file_info extends file_info_stored {
 521      public function get_parent() {
 522          if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
 523              return $this->browser->get_file_info($this->context);
 524          }
 525          return parent::get_parent();
 526      }
 527      public function get_visible_name() {
 528          if ($this->lf->get_filepath() === '/' and $this->lf->get_filename() === '.') {
 529              return $this->topvisiblename;
 530          }
 531          return parent::get_visible_name();
 532      }
 533  }
 534  
 535  function resource_set_mainfile($data) {
 536      global $DB;
 537      $fs = get_file_storage();
 538      $cmid = $data->coursemodule;
 539      $draftitemid = $data->files;
 540  
 541      $context = context_module::instance($cmid);
 542      if ($draftitemid) {
 543          $options = array('subdirs' => true, 'embed' => false);
 544          if ($data->display == RESOURCELIB_DISPLAY_EMBED) {
 545              $options['embed'] = true;
 546          }
 547          file_save_draft_area_files($draftitemid, $context->id, 'mod_resource', 'content', 0, $options);
 548      }
 549      $files = $fs->get_area_files($context->id, 'mod_resource', 'content', 0, 'sortorder', false);
 550      if (count($files) == 1) {
 551          // only one file attached, set it as main file automatically
 552          $file = reset($files);
 553          file_set_sortorder($context->id, 'mod_resource', 'content', 0, $file->get_filepath(), $file->get_filename(), 1);
 554      }
 555  }