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.

Differences Between: [Versions 401 and 402] [Versions 401 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  namespace editor_tiny;
  18  
  19  /**
  20   * Tiny Editor.
  21   *
  22   * @package    editor_tiny
  23   * @copyright  2021 Andrew Lyons <andrew@nicols.co.uk>
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  class editor extends \texteditor {
  27  
  28      /** @var manager The Tiny Manager instace */
  29      protected $manager;
  30  
  31      /** @var \stdClass|null The default configuration to use if none is provided */
  32      protected static $defaultconfiguration = null;
  33  
  34      /**
  35       * Instantiate the new editor instance.
  36       */
  37      public function __construct() {
  38          $this->manager = new manager();
  39      }
  40  
  41      /**
  42       * Set the default configuration for the editor.
  43       *
  44       * @param manager $manager The editor manager
  45       */
  46      public static function set_default_configuration(manager $manager): void {
  47          global $PAGE;
  48  
  49          if (self::is_default_configuration_set()) {
  50              return;
  51          }
  52  
  53          $context = $PAGE->context;
  54  
  55          $config = (object) [
  56              'css' => $PAGE->theme->editor_css_url()->out(false),
  57              'context' => $context->id,
  58              'plugins' => $manager->get_plugin_configuration($context, [], []),
  59          ];
  60  
  61          $config = json_encode($config);
  62          $inlinejs = <<<EOF
  63              M.util.js_pending('editor_tiny/editor:defaultConfiguration');
  64              require(['editor_tiny/editor'], (Tiny) => {
  65                  Tiny.configureDefaultEditor({$config});
  66                  M.util.js_complete('editor_tiny/editor:defaultConfiguration');
  67              });
  68          EOF;
  69  
  70          $PAGE->requires->js_amd_inline($inlinejs);
  71  
  72          self::$defaultconfiguration = $config;
  73      }
  74  
  75      /**
  76       * Fetch the current defautl configuration.
  77       *
  78       * @return \stdClass|null The default configuration or null if not set.
  79       */
  80      public static function get_default_configuration(): ?\stdClass {
  81          return self::$defaultconfiguration;
  82      }
  83  
  84      /**
  85       * Reset the default configuration.
  86       */
  87      public static function reset_default_configuration(): void {
  88          self::$defaultconfiguration = null;
  89      }
  90  
  91      /**
  92       * Check if the default configuration is set.
  93       *
  94       * @return bool True if the default configuration is set.
  95       */
  96      public static function is_default_configuration_set(): bool {
  97          return !empty(self::$defaultconfiguration);
  98      }
  99  
 100      /**
 101       * Is the current browser supported by this editor?
 102       *
 103       * @return bool
 104       */
 105      public function supported_by_browser() {
 106          return true;
 107      }
 108  
 109      /**
 110       * List of supported text field formats.
 111       *
 112       * @return array
 113       */
 114      public function get_supported_formats() {
 115          return [
 116              FORMAT_HTML => FORMAT_HTML,
 117          ];
 118      }
 119  
 120      /**
 121       * Returns text format preferred by this editor.
 122       *
 123       * @return int
 124       */
 125      public function get_preferred_format() {
 126          return FORMAT_HTML;
 127      }
 128  
 129      /**
 130       * Does this editor support picking from repositories?
 131       *
 132       * @return bool
 133       */
 134      public function supports_repositories() {
 135          return true;
 136      }
 137  
 138      /**
 139       * Use this editor for given element.
 140       *
 141       * @param string $elementid
 142       * @param array $options
 143       * @param null $fpoptions
 144       */
 145      public function use_editor($elementid, array $options = null, $fpoptions = null) {
 146          global $PAGE;
 147  
 148          // Ensure that the default configuration is set.
 149          self::set_default_configuration($this->manager);
 150  
 151          if ($fpoptions === null) {
 152              $fpoptions = [];
 153          }
 154  
 155          $context = $PAGE->context;
 156  
 157          if (isset($options['context']) && ($options['context'] instanceof \context)) {
 158              // A different context was provided.
 159              // Use that instead.
 160              $context = $options['context'];
 161          }
 162  
 163          // Generate the configuration for this editor.
 164          $siteconfig = get_config('editor_tiny');
 165          $config = (object) [
 166              // The URL to the CSS file for the editor.
 167              'css' => $PAGE->theme->editor_css_url()->out(false),
 168  
 169              // The current context for this page or editor.
 170              'context' => $context->id,
 171  
 172              // File picker options.
 173              'filepicker' => $fpoptions,
 174  
 175              'currentLanguage' => current_language(),
 176  
 177              'branding' => property_exists($siteconfig, 'branding') ? !empty($siteconfig->branding) : true,
 178  
 179              // Language options.
 180              'language' => [
 181                  'currentlang' => current_language(),
 182                  'installed' => get_string_manager()->get_list_of_translations(true),
 183                  'available' => get_string_manager()->get_list_of_languages()
 184              ],
 185  
 186              // Placeholder selectors.
 187              // Some contents (Example: placeholder elements) are only shown in the editor, and not to users. It is unrelated to the
 188              // real display. We created a list of placeholder selectors, so we can decide to or not to apply rules, styles... to
 189              // these elements.
 190              // The default of this list will be empty.
 191              // Other plugins can register their placeholder elements to placeholderSelectors list by calling
 192              // editor_tiny/options::registerPlaceholderSelectors.
 193              'placeholderSelectors' => [],
 194  
 195              // Plugin configuration.
 196              'plugins' => $this->manager->get_plugin_configuration($context, $options, $fpoptions, $this),
 197  
 198              // Nest menu inside parent DOM.
 199              'nestedmenu' => true,
 200          ];
 201  
 202          if (defined('BEHAT_SITE_RUNNING') && BEHAT_SITE_RUNNING) {
 203              // Add sample selectors for Behat test.
 204              $config->placeholderSelectors = ['.behat-tinymce-placeholder'];
 205          }
 206  
 207          foreach ($fpoptions as $fp) {
 208              // Guess the draftitemid for the editor.
 209              // Note: This is the best we can do at the moment.
 210              if (!empty($fp->itemid)) {
 211                  $config->draftitemid = $fp->itemid;
 212                  break;
 213              }
 214          }
 215  
 216          $configoptions = json_encode(convert_to_array($config));
 217  
 218          // Note: This is not ideal but the editor does not have control over any HTML output.
 219          // The Editor API only allows you to run JavaScript.
 220          // In the future we will extend the editor API to allow it to generate the textarea, or attributes to use in the
 221          // textarea or its wrapper.
 222          // For now we cannot use the `js_call_amd()` API call because it warns if the parameters passed exceed a
 223          // relatively low character limit.
 224          $config = json_encode($config);
 225          $inlinejs = <<<EOF
 226              M.util.js_pending('editor_tiny/editor');
 227              require(['editor_tiny/editor'], (Tiny) => {
 228                  Tiny.setupForElementId({
 229                      elementId: "$elementid}",
 230                      options: $configoptions},
 231                  });
 232                  M.util.js_complete('editor_tiny/editor');
 233              });
 234          EOF;
 235  
 236          $PAGE->requires->js_amd_inline($inlinejs);
 237      }
 238  }