PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/filter/mediaplugin/filter.php

https://github.com/pauln/moodle
PHP | 166 lines | 71 code | 23 blank | 72 comment | 19 complexity | e7a19303c8e96e12effb0f421bcae536 MD5 | raw file
  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. * Media plugin filtering
  18. *
  19. * This filter will replace any links to a media file with
  20. * a media plugin that plays that media inline
  21. *
  22. * @package filter
  23. * @subpackage mediaplugin
  24. * @copyright 2004 onwards Martin Dougiamas {@link http://moodle.com}
  25. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26. */
  27. defined('MOODLE_INTERNAL') || die();
  28. /**
  29. * Automatic media embedding filter class.
  30. *
  31. * It is highly recommended to configure servers to be compatible with our slasharguments,
  32. * otherwise the "?d=600x400" may not work.
  33. *
  34. * @package filter
  35. * @subpackage mediaplugin
  36. * @copyright 2004 onwards Martin Dougiamas {@link http://moodle.com}
  37. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38. */
  39. class filter_mediaplugin extends moodle_text_filter {
  40. /** @var bool True if currently filtering trusted text */
  41. private $trusted;
  42. /** @var core_media_renderer Media renderer */
  43. private $mediarenderer;
  44. /** @var string Partial regex pattern indicating possible embeddable content */
  45. private $embedmarkers;
  46. public function filter($text, array $options = array()) {
  47. global $CFG, $PAGE;
  48. if (!is_string($text) or empty($text)) {
  49. // non string data can not be filtered anyway
  50. return $text;
  51. }
  52. if (stripos($text, '</a>') === false) {
  53. // Performance shortcut - if not </a> tag, nothing can match.
  54. return $text;
  55. }
  56. if (!$this->mediarenderer) {
  57. $this->mediarenderer = $PAGE->get_renderer('core', 'media');
  58. $this->embedmarkers = $this->mediarenderer->get_embeddable_markers();
  59. }
  60. // Check SWF permissions.
  61. $this->trusted = !empty($options['noclean']) or !empty($CFG->allowobjectembed);
  62. // Looking for tags.
  63. $matches = preg_split('/(<[^>]*>)/i', $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
  64. if (!$matches) {
  65. return $text;
  66. }
  67. // Regex to find media extensions in an <a> tag.
  68. $re = '~<a\s[^>]*href="([^"]*(?:' . $this->embedmarkers . ')[^"]*)"[^>]*>([^>]*)</a>~is';
  69. $newtext = '';
  70. $validtag = '';
  71. $sizeofmatches = count($matches);
  72. // We iterate through the given string to find valid <a> tags
  73. // and build them so that the callback function can check it for
  74. // embedded content. Then we rebuild the string.
  75. foreach ($matches as $idx => $tag) {
  76. if (preg_match('|</a>|', $tag) && !empty($validtag)) {
  77. $validtag .= $tag;
  78. // Given we now have a valid <a> tag to process it's time for
  79. // ReDoS protection. Stop processing if a word is too large.
  80. if (strlen($validtag) < 4096) {
  81. $processed = preg_replace_callback($re, array($this, 'callback'), $validtag);
  82. }
  83. // Rebuilding the string with our new processed text.
  84. $newtext .= !empty($processed) ? $processed : $validtag;
  85. // Wipe it so we can catch any more instances to filter.
  86. $validtag = '';
  87. $processed = '';
  88. } else if (preg_match('/<a\s[^>]*/', $tag) && $sizeofmatches > 1) {
  89. // Looking for a starting <a> tag.
  90. $validtag = $tag;
  91. } else {
  92. // If we have a validtag add to that to process later,
  93. // else add straight onto our newtext string.
  94. if (!empty($validtag)) {
  95. $validtag .= $tag;
  96. } else {
  97. $newtext .= $tag;
  98. }
  99. }
  100. }
  101. // Return the same string except processed by the above.
  102. return $newtext;
  103. }
  104. /**
  105. * Replace link with embedded content, if supported.
  106. *
  107. * @param array $matches
  108. * @return string
  109. */
  110. private function callback(array $matches) {
  111. global $CFG, $PAGE;
  112. // Check if we ignore it.
  113. if (preg_match('/class="[^"]*nomediaplugin/i', $matches[0])) {
  114. return $matches[0];
  115. }
  116. // Get name.
  117. $name = trim($matches[2]);
  118. if (empty($name) or strpos($name, 'http') === 0) {
  119. $name = ''; // Use default name.
  120. }
  121. // Split provided URL into alternatives.
  122. $urls = core_media::split_alternatives($matches[1], $width, $height);
  123. $options = array();
  124. // Allow SWF (or not).
  125. if ($this->trusted) {
  126. $options[core_media::OPTION_TRUSTED] = true;
  127. }
  128. // We could test whether embed is possible using can_embed, but to save
  129. // time, let's just embed it with the 'fallback to blank' option which
  130. // does most of the same stuff anyhow.
  131. $options[core_media::OPTION_FALLBACK_TO_BLANK] = true;
  132. // NOTE: Options are not passed through from filter because the 'embed'
  133. // code does not recognise filter options (it's a different kind of
  134. // option-space) as it can be used in non-filter situations.
  135. $result = $this->mediarenderer->embed_alternatives($urls, $name, $width, $height, $options);
  136. // If something was embedded, return it, otherwise return original.
  137. if ($result !== '') {
  138. return $result;
  139. } else {
  140. return $matches[0];
  141. }
  142. }
  143. }