Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-35365

Allow repository plug-ins determine how to preview it's files



    • Affected Branches:



      I'm creating a video repository for Kaltura where all videos are uploaded and housed on a remote server. Nothing is downloaded onto the Moodle file system. All media on Kaltura is reference via a unique URL (similar to YouTube).

      The purpose of the plug-in is to allow the user to browse and select videos that belong to their Kaltura account. None of the Kaltura videos are physically stored in Moodle, instead the Kaltura repository plug-in uses the external file repository type and uses a URL address that pertains to a specific media file (for example: http://www.kaltura.com/index.php/kwidget/wid/_1234/uiconf_id/456/entry_id/asdf123/v/flash#<video name>). Navigating to this link will display the video in a Kaltura branded flash player.

      However, in Moodle 2.3 and 2.4 there is a problem when previewing a video selected from the Kaltura repository plug-in. When a video is selected, the preview iframe is blank (in the case of Moodle 2.4 it returns an error message).

      I've been able to determine the cause of the problem in Moodle 2.3 and 2.4, when it previously was not a problem in Moodle 2.0 - 2.2.

      In pre Moodle 2.3 the core code was written to specifically handle youtube links in order for it to display properly (details in MDL-33390). Luckily I was able to add a workaround to have the Kaltura videos display properly. The workaround was to add a /v/flash#video_name to the end of the URL to instruct Moodle to create the proper embed markup.

      With Moodle 2.3 (and 2.4) this system has changed with the introduction of using an iframe in the preview dialog and relying on lib/medialib.php to determine the file extension (or URL) and using a player that suits the cirteria (similar to how the filter plug-ins work). Youtube (which is a video repository much like Kaltura is) gets core treatment in this area in order for it to work properly.

      The main problem is that the Kaltura video links do not match any criteria used by Moodle standard media players in addition to the fact that there is no way for a repository plug-in to determine how to preview the selected file. The former is not an issue becuase a filter plug-in can be created to handle the links. However the latter is the issue of most concern as core code handles it.

      Examples in the code

      The following code snippets (from Moodle 2.3) are meant to help illustrate the problem I'm having with the preview code.

      In the Kaltura repository code, a video listing is generated. Kaltura has the ability for user to upload video, images and music files. The purpose of the $video->name . '.mpg' line is to force Moodle to display a video listing icon (when not viewing files in icon mode in the file picker). The source URL is created and all other array elements are generically created. Notice the source url http://www.kaltura.com/index.php/kwidget/wid/_787032/uiconf_id/6709411/entry_id/1_h2no1nvq/v/flash

              switch ($video->mediaType) {
                  case KalturaMediaType::AUDIO:  // May need a special case to handle audio files
                  case KalturaMediaType::VIDEO:
                      $name = $video->name . '.mpg'; // Manually adding an image extension.  This is only to force moodle to display the correct icons
                      $source = $uri .'/index.php/kwidget/wid/_'.$partner_id.
                                '/uiconf_id/'.$uiconf_id.'/entry_id/' . $video->id . '/v/flash#'.
                                $video->name .'';
                  case KalturaMediaType::IMAGE:
                      $name = $video->name . '.png'; // Manually adding an image extension.  This is only to force moodle to display the correct icons
                      $source = $video->thumbnailUrl . '/height/200/width/300/type/1/v/flash#'. $video->name;
                      $name   = 'Unknown Media Type';
                      $source = 'Unknown Media Type';
              $results[] = array('title' => $name,
                                 'shorttitle' => $video->name,
                                 'date' => userdate($video->updatedAt),
                                 'thumbnail' => $video->thumbnailUrl,
                                 'thumbnail_width' => 150,
                                 'thumbnail_height' => 70,
                                 'source' =>  $source,
                                 'hasauthor' => true,
                                 //'url' => '',
                                 'haslicense' => true

      When the user selects a file from the repository, the lib/editor/tinymce/tiny_mce/ script changes the inner HTML of a div to an iframe where the source url is *http://localhost/ack/kaltura.git/lib/editor/tinymce/tiny_mce/*.

      function generatePreview(c) {
          var f = document.forms[0], p = document.getElementById('prev');
          p.innerHTML = '<!-- x --->';
          var re = new RegExp("(.+)\#(.+)", "i");
          var result = f.src.value.match(re);
          if (result) {
              f.src.value = result[1];
              f.filename.value = result[2];
          } else {
              f.src.value = f.src.value;
              f.filename.value = f.src.value;
          // After constrain
          var pl = serializeParameters();
          if (pl == '') {
                  p.innerHTML = '';
          pl = tinyMCEPopup.editor.plugins.moodlemedia._parse(pl);
          if (!pl.src) {
                  p.innerHTML = '';
          pl.src = tinyMCEPopup.editor.documentBaseURI.toAbsolute(pl.src);
          // NOTE: Do not try to prevent https security popups here - users would get them later on real page anyway!
          // We can not include URL directly in parameters because some security filters might block it.
          p.innerHTML = '<iframe src="' + tinyMCE.baseURL + '/plugins/moodlemedia/preview.php'
              + '?path=' + encodeURIComponent(encode64(pl.src.toString()))
              + '&sesskey=' + encodeURIComponent(parent.M.cfg.sesskey)
              + '" width="100%" height="100%"></iframe>';

      In preview.php the core media renderer is passed the URL. Inside the core media renderer all of the media players are passed the URL to determine whether it is a file that can be played. Most of them check the extension of the file contained in the URL. The youtube media player class checks to see if www.youtube.com is found in the URL; then returns the embed markup needed to display a Youtube video

      // URL to the media.
      $path = required_param('path', PARAM_RAW);
      $path = base64_decode($path);
      $url = clean_param($path, PARAM_URL);
      $url = new moodle_url($url);
      $editor = new tinymce_texteditor();
      // Now output this file which is super-simple.
      $PAGE->set_url(new moodle_url('/lib/editor/tinymce/tiny_mce/'.$editor->version.'/plugins/moodlemedia/preview.php',
              array('path' => base64_encode($path))));
      echo $OUTPUT->header();
      $mediarenderer = $PAGE->get_renderer('core', 'media');
      if ($mediarenderer->can_embed_url($url)) {
          echo $mediarenderer->embed_url($url);
      echo $url;
      echo $OUTPUT->footer();

       * Player that creates YouTube embedding.
       * @copyright 2011 The Open University
       * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
      class core_media_player_youtube extends core_media_player_external {
          protected function embed_external(moodle_url $url, $name, $width, $height, $options) {
              global $CFG;
              $site = 'www.youtube.com';
              $videoid = end($this->matches);
              $info = trim($name);
              if (empty($info) or strpos($info, 'http') === 0) {
                  $info = get_string('siteyoutube', 'core_media');
              $info = s($info);
              self::pick_video_size($width, $height);
              if (empty($CFG->xmlstrictheaders)) {
                  return <<<OET
      <span class="mediaplugin mediaplugin_youtube">
      <iframe title="$info" width="$width" height="$height" src="https://$site/embed/$videoid?rel=0&wmode=transparent" frameborder="0" allowfullscreen="1"></iframe>
              // NOTE: we can not use any link fallback because it breaks built-in
              // player on iOS devices.
              $output = <<<OET
      <span class="mediaplugin mediaplugin_youtube">
      <object title="$info" type="application/x-shockwave-flash"
        data="https://$site/v/$videoid&amp;fs=1&amp;rel=0" width="$width" height="$height">
       <param name="movie" value="$site/v/$videoid&amp;fs=1&amp;rel=0" />
       <param name="FlashVars" value="playerMode=embedded" />
       <param name="allowFullScreen" value="true" />
              return $output;
          protected function get_regex() {
              // Regex for standard youtube link
               $link = '(youtube(-nocookie)?\.com/(?:watch\?v=|v/))';
              // Regex for shortened youtube link
              $shortlink = '((youtu|y2u)\.be/)';
              // Initial part of link.
               $start = '~^https?://(www\.)?(' . $link . '|' . $shortlink . ')';
              // Middle bit: Video key value
              $middle = '([a-z0-9\-_]+)';
              return $start . $middle . core_media_player_external::END_LINK_REGEX_PART;
          public function get_rank() {
              // I decided to make the link-embedding ones (that don't handle file
              // formats) have ranking in the 1000 range.
              return 1001;
          public function get_embeddable_markers() {
              return array('youtube.com', 'youtube-nocookie.com', 'youtu.be', 'y2u.be');

      As you can see the core code doesn't allow for external media repositories (that use URL with no file extention information) to properly preview the coutent before it is inserted by the user.


      Allow repository plug-ins to generate their own embed/preview markup that can be used by the preview popup. Have an abstract preview class that can be overridden by a repository sub class, where embed markup can be returned to preview.php and the output echoed back to the iframe inner HTML.

      The preview.php script can determine exactly which repository plug-in the URL is from if it is passed the repository id; once the id is passed to preview.php it can use the repository static method public static function get_repository_by_id($repositoryid, $context, $options = array()) and then call the preview overridden method.

      Why change the way it works

      1. Media repositories that work along the same lines as Youtube will run into the same difficult where Moodle core renderer will not know what to do with the URL.
      2. Greater flexibility to determine how to handle the previewing of a file (example: having a company branded player used to preview the media file, or better handling of a non standard media format)
      3. When changes need to be made maintainers only need to update code for their plug-in and not core rendering code

      Let's open a discussion

      I'm interested in hearing your thoughs about this proposal. As it has an effect on Moodle 2.3 and Moodle 2.4 (I've already tested against master) which is currently in development and I would like to create a plug-in that is fully supported by Moodle's core code.



          Issue Links



              adelamarre Akin Delamarre
              Component watchers:
              Adrian Greeve, Jake Dallimore, Mathew May, Mihail Geshoski, Peter Dias, Adrian Greeve, Jake Dallimore, Mathew May, Mihail Geshoski, Peter Dias
              1 Vote for this issue
              3 Start watching this issue