Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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   * TinyMCE text editor integration.
  19   *
  20   * @package    editor
  21   * @subpackage tinymce
  22   * @copyright  2009 Petr Skoda (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  class tinymce_texteditor extends texteditor {
  29      /** @var string active version - this is the directory name where to find tinymce code */
  30      public $version = '3.5.11';
  31  
  32      /**
  33       * Is the current browser supported by this editor?
  34       * @return bool
  35       */
  36      public function supported_by_browser() {
  37          // We don't support any browsers which it doesn't support.
  38          return true;
  39      }
  40  
  41      /**
  42       * Returns array of supported text formats.
  43       * @return array
  44       */
  45      public function get_supported_formats() {
  46          // FORMAT_MOODLE is not supported here, sorry.
  47          return array(FORMAT_HTML => FORMAT_HTML);
  48      }
  49  
  50      /**
  51       * Returns text format preferred by this editor.
  52       * @return int
  53       */
  54      public function get_preferred_format() {
  55          return FORMAT_HTML;
  56      }
  57  
  58      /**
  59       * Does this editor support picking from repositories?
  60       * @return bool
  61       */
  62      public function supports_repositories() {
  63          return true;
  64      }
  65  
  66      /**
  67       * Sets up head code if necessary.
  68       */
  69      public function head_setup() {
  70      }
  71  
  72      /**
  73       * Use this editor for give element.
  74       *
  75       * @param string $elementid
  76       * @param array $options
  77       * @param null $fpoptions
  78       */
  79      public function use_editor($elementid, array $options=null, $fpoptions=null) {
  80          global $PAGE, $CFG;
  81          // Note: use full moodle_url instance to prevent standard JS loader, make sure we are using https on profile page if required.
  82          if ($CFG->debugdeveloper) {
  83              $PAGE->requires->js(new moodle_url('/lib/editor/tinymce/tiny_mce/'.$this->version.'/tiny_mce_src.js'));
  84          } else {
  85              $PAGE->requires->js(new moodle_url('/lib/editor/tinymce/tiny_mce/'.$this->version.'/tiny_mce.js'));
  86          }
  87          $PAGE->requires->js_init_call('M.editor_tinymce.init_editor', array($elementid, $this->get_init_params($elementid, $options)), true);
  88          if ($fpoptions) {
  89              $PAGE->requires->js_init_call('M.editor_tinymce.init_filepicker', array($elementid, $fpoptions), true);
  90          }
  91      }
  92  
  93      protected function get_init_params($elementid, array $options=null) {
  94          global $CFG, $PAGE, $OUTPUT;
  95  
  96          //TODO: we need to implement user preferences that affect the editor setup too
  97  
  98          $directionality = get_string('thisdirection', 'langconfig');
  99          $strtime        = get_string('strftimetime');
 100          $strdate        = get_string('strftimedaydate');
 101          $lang           = current_language();
 102          $contentcss     = $PAGE->theme->editor_css_url()->out(false);
 103  
 104          $context = empty($options['context']) ? context_system::instance() : $options['context'];
 105  
 106          $config = get_config('editor_tinymce');
 107          if (!isset($config->disabledsubplugins)) {
 108              $config->disabledsubplugins = '';
 109          }
 110  
 111          // Remove the manage files button if requested.
 112          if (isset($options['enable_filemanagement']) && !$options['enable_filemanagement']) {
 113              if (!strpos($config->disabledsubplugins, 'managefiles')) {
 114                  $config->disabledsubplugins .= ',managefiles';
 115              }
 116          }
 117  
 118          $fontselectlist = empty($config->fontselectlist) ? '' : $config->fontselectlist;
 119  
 120          $langrev = -1;
 121          if (!empty($CFG->cachejs)) {
 122              $langrev = get_string_manager()->get_revision();
 123          }
 124  
 125          $params = array(
 126              'moodle_config' => $config,
 127              'mode' => "exact",
 128              'elements' => $elementid,
 129              'relative_urls' => false,
 130              'document_base_url' => $CFG->wwwroot,
 131              'moodle_plugin_base' => "$CFG->wwwroot/lib/editor/tinymce/plugins/",
 132              'content_css' => $contentcss,
 133              'language' => $lang,
 134              'directionality' => $directionality,
 135              'plugin_insertdate_dateFormat ' => $strdate,
 136              'plugin_insertdate_timeFormat ' => $strtime,
 137              'theme' => "advanced",
 138              'skin' => "moodle",
 139              'apply_source_formatting' => true,
 140              'remove_script_host' => false,
 141              'entity_encoding' => "raw",
 142              'plugins' => 'lists,table,style,layer,advhr,advlink,emotions,inlinepopups,' .
 143                  'searchreplace,paste,directionality,fullscreen,nonbreaking,contextmenu,' .
 144                  'insertdatetime,save,iespell,preview,print,noneditable,visualchars,' .
 145                  'xhtmlxtras,template,pagebreak',
 146              'gecko_spellcheck' => true,
 147              'theme_advanced_font_sizes' => "1,2,3,4,5,6,7",
 148              'theme_advanced_layout_manager' => "SimpleLayout",
 149              'theme_advanced_toolbar_align' => "left",
 150              'theme_advanced_fonts' => $fontselectlist,
 151              'theme_advanced_resize_horizontal' => true,
 152              'theme_advanced_resizing' => true,
 153              'theme_advanced_resizing_min_height' => 30,
 154              'min_height' => 30,
 155              'theme_advanced_toolbar_location' => "top",
 156              'theme_advanced_statusbar_location' => "bottom",
 157              'language_load' => false, // We load all lang strings directly from Moodle.
 158              'langrev' => $langrev,
 159          );
 160  
 161          // Should we override the default toolbar layout unconditionally?
 162          if (!empty($config->customtoolbar) and $customtoolbar = self::parse_toolbar_setting($config->customtoolbar)) {
 163              $i = 1;
 164              foreach ($customtoolbar as $line) {
 165                  $params['theme_advanced_buttons'.$i] = $line;
 166                  $i++;
 167              }
 168          } else {
 169              // At least one line is required.
 170              $params['theme_advanced_buttons1'] = '';
 171          }
 172  
 173          if (!empty($config->customconfig)) {
 174              $config->customconfig = trim($config->customconfig);
 175              $decoded = json_decode($config->customconfig, true);
 176              if (is_array($decoded)) {
 177                  foreach ($decoded as $k=>$v) {
 178                      $params[$k] = $v;
 179                  }
 180              }
 181          }
 182  
 183          if (!empty($options['legacy']) or !empty($options['noclean']) or !empty($options['trusted'])) {
 184              // now deal somehow with non-standard tags, people scream when we do not make moodle code xtml strict,
 185              // but they scream even more when we strip all tags that are not strict :-(
 186              $params['valid_elements'] = 'script[src|type],*[*]'; // for some reason the *[*] does not inlcude javascript src attribute MDL-25836
 187              $params['invalid_elements'] = '';
 188          }
 189          // Add unique moodle elements - unfortunately we have to decide if these are SPANs or DIVs.
 190          $params['extended_valid_elements'] = 'nolink,tex,algebra,lang[lang]';
 191          $params['custom_elements'] = 'nolink,~tex,~algebra,lang';
 192  
 193          //Add onblur event for client side text validation
 194          if (!empty($options['required'])) {
 195              $params['init_instance_callback'] = 'M.editor_tinymce.onblur_event';
 196          }
 197  
 198          // Allow plugins to adjust parameters.
 199          editor_tinymce_plugin::all_update_init_params($params, $context, $options);
 200  
 201          // Remove temporary parameters.
 202          unset($params['moodle_config']);
 203  
 204          return $params;
 205      }
 206  
 207      /**
 208       * Parse the custom toolbar setting.
 209       * @param string $customtoolbar
 210       * @return array csv toolbar lines
 211       */
 212      public static function parse_toolbar_setting($customtoolbar) {
 213          $result = array();
 214          $customtoolbar = trim($customtoolbar);
 215          if ($customtoolbar === '') {
 216              return $result;
 217          }
 218          $customtoolbar = str_replace("\r", "\n", $customtoolbar);
 219          $customtoolbar = strtolower($customtoolbar);
 220          $i = 0;
 221          foreach (explode("\n", $customtoolbar) as $line) {
 222              $line = preg_replace('/[^a-z0-9_,\|\-]/', ',', $line);
 223              $line = str_replace('|', ',|,', $line);
 224              $line = preg_replace('/,,+/', ',', $line);
 225              $line = trim($line, ',|');
 226              if ($line === '') {
 227                  continue;
 228              }
 229              if ($i == 10) {
 230                  // Maximum is ten lines, merge the rest to the last line.
 231                  $result[9] = $result[9].','.$line;
 232              } else {
 233                  $result[] = $line;
 234                  $i++;
 235              }
 236          }
 237          return $result;
 238      }
 239  
 240      /**
 241       * Gets a named plugin object. Will cause fatal error if plugin doesn't
 242       * exist. This is intended for use by plugin files themselves.
 243       *
 244       * @param string $plugin Name of plugin e.g. 'moodleemoticon'
 245       * @return editor_tinymce_plugin Plugin object
 246       */
 247      public function get_plugin($plugin) {
 248          global $CFG;
 249          return editor_tinymce_plugin::get($plugin);
 250      }
 251  
 252      /**
 253       * Equivalent to tinyMCE.baseURL value available from JavaScript,
 254       * always use instead of /../ when referencing tinymce core code from moodle plugins!
 255       *
 256       * @return moodle_url url pointing to the root of TinyMCE javascript code.
 257       */
 258      public function get_tinymce_base_url() {
 259          global $CFG;
 260          return new moodle_url("/lib/editor/tinymce/tiny_mce/$this->version/");
 261      }
 262  
 263  }