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  use context;
  20  
  21  /**
  22   * Tiny Editor Plugin manager.
  23   *
  24   * @package    editor_tiny
  25   * @copyright  2021 Andrew Lyons <andrew@nicols.co.uk>
  26   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  class manager {
  29  
  30      /**
  31       * Get the configuration for all plugins.
  32       *
  33       * @param context $context The context that the editor is used within
  34       * @param array $options The options passed in when requesting the editor
  35       * @param array $fpoptions The filepicker options passed in when requesting the editor
  36       * @param  editor $editor The editor instance in which the plugin is initialised
  37       */
  38      public function get_plugin_configuration(
  39          context $context,
  40          array $options = [],
  41          array $fpoptions = [],
  42          ?editor $editor = null
  43      ): array {
  44          $disabledplugins = $this->get_disabled_plugins();
  45  
  46          // Get the list of plugins.
  47          // Note: Disabled plugins are already removed from this list.
  48          $plugins = $this->get_shipped_plugins();
  49  
  50          // Fetch configuration for Moodle plugins.
  51          $moodleplugins = \core_component::get_plugin_list_with_class('tiny', 'plugininfo');
  52          foreach ($moodleplugins as $plugin => $classname) {
  53              if (in_array($plugin, $disabledplugins) || in_array("{$plugin}/plugin", $disabledplugins)) {
  54                  // Skip getting data for disabled plugins.
  55                  continue;
  56              }
  57  
  58              if (!is_a($classname, plugin::class, true)) {
  59                  // Skip plugins that do not implement the plugin interface.
  60                  debugging("Plugin {$plugin} does not implement the plugin interface", DEBUG_DEVELOPER);
  61                  continue;
  62              }
  63  
  64              if (!$classname::is_enabled($context, $options, $fpoptions, $editor)) {
  65                  // This plugin has disabled itself for some reason.
  66                  // This is typical for media plugins where there is no file storage.
  67                  continue;
  68              }
  69  
  70              // Get the plugin information, which includes the list of buttons, menu items, and configuration.
  71              $plugininfo = $classname::get_plugin_info(
  72                  $context,
  73                  $options,
  74                  $fpoptions,
  75                  $editor
  76              );
  77  
  78              if (!empty($config)) {
  79                  $plugininfo['config'] = $config;
  80              }
  81  
  82              // We suffix the plugin name for Moodle plugins with /plugin to avoid conflicts with Tiny plugins.
  83              $plugins["{$plugin}/plugin"] = $plugininfo;
  84          }
  85  
  86          return $plugins;
  87      }
  88  
  89      /**
  90       * Get a list of the buttons provided by this plugin.
  91       *
  92       * @return string[]
  93       */
  94      protected function get_tinymce_buttons(): array {
  95          // The following list is defined at:
  96          // https://www.tiny.cloud/docs/advanced/available-toolbar-buttons/#thecoretoolbarbuttons.
  97          return [
  98              // These are always available, without requiring additional plugins.
  99              'aligncenter',
 100              'alignjustify',
 101              'alignleft',
 102              'alignnone',
 103              'alignright',
 104              'blockquote',
 105              'backcolor',
 106              'bold',
 107              'copy',
 108              'cut',
 109              'fontselect',
 110              'fontsizeselect',
 111              'forecolor',
 112              'formatselect',
 113              'h1',
 114              'h2',
 115              'h3',
 116              'h4',
 117              'h5',
 118              'h6',
 119              'indent',
 120              'italic',
 121              'language',
 122              'lineheight',
 123              'newdocument',
 124              'outdent',
 125              'paste',
 126              'redo',
 127              'remove',
 128              'removeformat',
 129              'selectall',
 130              'strikethrough',
 131              'styleselect',
 132              'subscript',
 133              'superscript',
 134              'underline',
 135              'undo',
 136              'visualaid',
 137          ];
 138      }
 139  
 140      /**
 141       * Get a list of the menu items provided by this plugin.
 142       *
 143       * @return string[]
 144       */
 145      protected function get_tinymce_menuitems(): array {
 146          // The following list is defined at:
 147          // https://www.tiny.cloud/docs/advanced/available-menu-items/#thecoremenuitems.
 148          return [
 149              'align' => 'format',
 150              'backcolor' => 'format',
 151              'blockformats' => 'format',
 152              'bold' => 'format',
 153              'codeformat' => 'format',
 154              'copy' => 'copy',
 155              'cut' => 'copy',
 156              'forecolor' => 'format',
 157              'formats' => 'format',
 158              'fontformats' => 'format',
 159              'fontsizes' => 'format',
 160              'italic' => 'format',
 161              'language' => 'format',
 162              'lineheight' => 'format',
 163              'newdocument' => 'file',
 164              'paste' => 'copy',
 165              'redo' => 'copy',
 166              'removeformat' => 'format',
 167              'selectall' => 'edit',
 168              'strikethrough' => 'format',
 169              'subscript' => 'format',
 170              'superscript' => 'format',
 171              'underline' => 'format',
 172              'undo' => 'copy',
 173              'visualaid' => 'view',
 174          ];
 175      }
 176  
 177      /**
 178       * Return a list of all available plugins, including both TinyMCE shipped, and Moodle add-onis.
 179       *
 180       * Each plugin is returned as an array element containing:
 181       * - a list of buttons (if applicable); and
 182       * - a list of menuitems (if applicable).
 183       *
 184       * Note: Not all plugins include buttons, and not all plugins include menuitems.
 185       * These array keys are optional.
 186       *
 187       * @return array
 188       */
 189      protected function get_available_plugins(): array {
 190          $plugins = $this->get_shipped_plugins();
 191          $plugins += $this->get_moodle_plugins();
 192  
 193          $disabledplugins = $this->get_disabled_plugins();
 194          $plugins = array_filter($plugins, function ($plugin) use ($disabledplugins) {
 195              return !in_array($plugin, $disabledplugins);
 196          }, ARRAY_FILTER_USE_KEY);
 197  
 198          return $plugins;
 199      }
 200  
 201      /**
 202       * Return a list of all available plugins built into TinyMCE and not shipped as separate Moodle plugins.
 203       *
 204       * Each plugin is returned as an array element containing:
 205       * - a list of buttons (if applicable); and
 206       * - a list of menuitems (if applicable).
 207       *
 208       * Note: Not all plugins include buttons, and not all plugins include menuitems.
 209       * These array keys are optional.
 210       *
 211       * @return array
 212       */
 213      protected function get_shipped_plugins(): array {
 214          $plugins = $this->get_tinymce_plugins();
 215          if ($this->premium_plugins_enabled()) {
 216              $plugins += $this->get_premium_plugins();
 217          }
 218  
 219          $disabledplugins = $this->get_disabled_plugins();
 220          return array_filter($plugins, function($plugin) use ($disabledplugins) {
 221              return !in_array($plugin, $disabledplugins);
 222          }, ARRAY_FILTER_USE_KEY);
 223      }
 224  
 225      /**
 226       * Get a list of the core plugins with their button, and menuitem, configuration.
 227       *
 228       * @return array[]
 229       */
 230      protected function get_tinymce_plugins(): array {
 231          // The following list is defined at:
 232          // https://www.tiny.cloud/docs/advanced/available-toolbar-buttons/#thecoretoolbarbuttons.
 233          return [
 234              'anchor' => [
 235                  'buttons' => [
 236                      'anchor',
 237                  ],
 238                  'menuitems' => [
 239                      'anchor' => 'insert',
 240                  ],
 241              ],
 242              'autosave' => [
 243                  'buttons' => [
 244                      'restoredraft',
 245                  ],
 246                  'menuitems' => [
 247                      'restoredraft' => 'file',
 248                  ],
 249              ],
 250              'charmap' => [
 251                  'buttons' => [
 252                      'charmap',
 253                  ],
 254                  'menuitems' => [
 255                      'charmap' => 'insert',
 256                  ],
 257              ],
 258              'code' => [
 259                  'buttons' => [
 260                      'code',
 261                  ],
 262                  'menuitems' => [
 263                      'code' => 'view',
 264                  ],
 265              ],
 266              'codesample' => [
 267                  'buttons' => [
 268                      'codesample',
 269                  ],
 270                  'menutiems' => [
 271                      'codesample' => 'insert',
 272                  ],
 273              ],
 274              'directionality' => [
 275                  'buttons' => [
 276                      'ltr',
 277                      'rtl',
 278                  ],
 279              ],
 280              'emoticons' => [
 281                  'buttons' => [
 282                      'emoticons',
 283                  ],
 284                  'menuitems' => [
 285                      'emoticons' => 'insert',
 286                  ],
 287              ],
 288              'fullscreen' => [
 289                  'buttons' => [
 290                      'fullscreen',
 291                  ],
 292                  'menuitems' => [
 293                      'fullscreen' => 'view',
 294                  ],
 295              ],
 296              'help' => [
 297                  'buttons' => [
 298                      'help',
 299                  ],
 300                  'menuitems' => [
 301                      'help' => 'help',
 302                  ],
 303              ],
 304              'image' => [
 305                  'buttons' => [
 306                      'image',
 307                  ],
 308                  'menuitems' => [
 309                      'image' => 'insert',
 310                  ],
 311              ],
 312              'insertdatetime' => [
 313                  'buttons' => [
 314                      'insertdatetime',
 315                  ],
 316                  'menuitems' => [
 317                      'insertdatetime' => 'insert',
 318                  ],
 319              ],
 320              'link' => [
 321                  'buttons' => [
 322                      'link',
 323                      'openlink',
 324                      'unlink',
 325                  ],
 326                  'menuitems' => [
 327                      'link' => 'insert',
 328                  ],
 329              ],
 330              'lists' => [
 331                  'buttons' => [
 332                      'bullist',
 333                      'numlist',
 334                  ],
 335              ],
 336              'media' => [
 337                  'buttons' => [
 338                      'media',
 339                  ],
 340                  'menuitems' => [
 341                      'media' => 'insert',
 342                  ],
 343              ],
 344              'nonbreaking' => [
 345                  'buttons' => [
 346                      'nonbreaking',
 347                  ],
 348                  'menuitems' => [
 349                      'nonbreaking' => 'insert',
 350                  ],
 351              ],
 352              'pagebreak' => [
 353                  'buttons' => [
 354                      'pagebreak',
 355                  ],
 356                  'menuitems' => [
 357                      'pagebreak' => 'insert',
 358                  ],
 359              ],
 360              'preview' => [
 361                  'buttons' => [
 362                      'preview',
 363                  ],
 364                  'menuitems' => [
 365                      'preview' => 'file',
 366                  ],
 367              ],
 368              'quickbars' => [
 369                  'buttons' => [
 370                      'quickimage',
 371                      'quicklink',
 372                      'quicktable',
 373                  ],
 374              ],
 375              'save' => [
 376                  'buttons' => [
 377                      'cancel',
 378                      'save',
 379                  ],
 380              ],
 381              'searchreplace' => [
 382                  'buttons' => [
 383                      'searchreplace',
 384                  ],
 385                  'menuitems' => [
 386                      'searchreplace' => 'edit',
 387                  ],
 388              ],
 389              'table' => [
 390                  'buttons' => [
 391                      'table',
 392                      'tablecellprops',
 393                      'tablecopyrow',
 394                      'tablecutrow',
 395                      'tabledelete',
 396                      'tabledeletecol',
 397                      'tabledeleterow',
 398                      'tableinsertdialog',
 399                      'tableinsertcolafter',
 400                      'tableinsertcolbefore',
 401                      'tableinsertrowafter',
 402                      'tableinsertrowbefore',
 403                      'tablemergecells',
 404                      'tablepasterowafter',
 405                      'tablepasterowbefore',
 406                      'tableprops',
 407                      'tablerowprops',
 408                      'tablesplitcells',
 409                      'tableclass',
 410                      'tablecellclass',
 411                      'tablecellvalign',
 412                      'tablecellborderwidth',
 413                      'tablecellborderstyle',
 414                      'tablecaption',
 415                      'tablecellbackgroundcolor',
 416                      'tablecellbordercolor',
 417                      'tablerowheader',
 418                      'tablecolheader',
 419                  ],
 420                  'menuitems' => [
 421                      'inserttable' => 'table',
 422                      'tableprops' => 'table',
 423                      'deletetable' => 'table',
 424                      'cell' => 'table',
 425                      'tablemergecells' => 'table',
 426                      'tablesplitcells' => 'table',
 427                      'tablecellprops' => 'table',
 428                      'column' => 'table',
 429                      'tableinsertcolumnbefore' => 'table',
 430                      'tableinsertcolumnafter' => 'table',
 431                      'tablecutcolumn' => 'table',
 432                      'tablecopycolumn' => 'table',
 433                      'tablepastecolumnbefore' => 'table',
 434                      'tablepastecolumnafter' => 'table',
 435                      'tabledeletecolumn' => 'table',
 436                      'row' => 'table',
 437                      'tableinsertrowbefore' => 'table',
 438                      'tableinsertrowafter' => 'table',
 439                      'tablecutrow' => 'table',
 440                      'tablecopyrow' => 'table',
 441                      'tablepasterowbefore' => 'table',
 442                      'tablepasterowafter' => 'table',
 443                      'tablerowprops' => 'table',
 444                      'tabledeleterow' => 'table',
 445                  ],
 446              ],
 447              'template' => [
 448                  'buttons' => [
 449                      'template',
 450                  ],
 451                  'menuitems' => [
 452                      'template' => 'insert',
 453                  ],
 454              ],
 455              'visualblocks' => [
 456                  'buttons' => [
 457                      'visualblocks',
 458                  ],
 459                  'menuitems' => [
 460                      'visualblocks' => 'view',
 461                  ],
 462              ],
 463              'visualchars' => [
 464                  'buttons' => [
 465                      'visualchars',
 466                  ],
 467                  'menuitems' => [
 468                      'visualchars' => 'view',
 469                  ],
 470              ],
 471              'wordcount' => [
 472                  'buttons' => [
 473                      'wordcount',
 474                  ],
 475                  'menuitems' => [
 476                      'wordcount' => 'tools',
 477                  ],
 478              ],
 479          ];
 480      }
 481  
 482      /**
 483       * Get a list of the disabled plugins.
 484       *
 485       * @return string[]
 486       */
 487      protected function get_disabled_plugins(): array {
 488          return [
 489              // Disable the image and media plugins.
 490              // These are not generally compatible with Moodle.
 491              'image',
 492              'media',
 493  
 494              // Use the Moodle autosave plugin instead.
 495              'autosave',
 496  
 497              // Disable the Template plugin for now.
 498              'template',
 499  
 500              // Disable the preview plugin as it does not support Moodle filters.
 501              'preview',
 502  
 503              // Use the Moodle link plugin instead.
 504              'link',
 505          ];
 506      }
 507  
 508      /**
 509       * Get a list of the Moodle plugins with their button, and menuitem, configuration.
 510       *
 511       * @return array[]
 512       */
 513      protected function get_moodle_plugins(): array {
 514          $plugins = \core_component::get_plugin_list_with_class('tiny', 'plugininfo');
 515  
 516          $pluginconfig = [];
 517          foreach ($plugins as $pluginname => $classname) {
 518              if (!is_a($classname, plugin::class, true)) {
 519                  continue;
 520              }
 521              // Module name => [buttons, menuitems].
 522              $pluginconfig["{$pluginname}/plugin"] = $classname::get_plugin_info();
 523          }
 524  
 525          return $pluginconfig;
 526      }
 527  
 528      /**
 529       * Check whether premium plugins are configured and enabled.
 530       *
 531       * @return bool
 532       */
 533      protected function premium_plugins_enabled(): bool {
 534          return false;
 535      }
 536  
 537      /**
 538       * Get a list of the Tiny Premium plugins with their button, and menuitem, configuration.
 539       *
 540       * Note: This only includes _compatible_ premium plugins.
 541       * Some premium plugins *may not* be compatible with Moodle, and some may require additional configuration.
 542       *
 543       * @return array[]
 544       */
 545      protected function get_premium_plugins(): array {
 546          return [
 547              'a11ycheck' => [
 548                  'buttons' => [
 549                      'a11ycheck',
 550                  ],
 551                  'menuitems' => [
 552                      'a11ycheck',
 553                  ],
 554              ],
 555              'advcode' => [
 556                  'buttons' => [
 557                      'code',
 558                  ],
 559                  'menuitems' => [
 560                      'code',
 561                  ],
 562              ],
 563              'footnotes' => [
 564                  'buttons' => [
 565                      'footnotes',
 566                      'footnotesupdate',
 567                  ],
 568                  'menuitems' => [
 569                      'footnotes',
 570                      'footnotesupdate',
 571                  ],
 572              ],
 573              'mergetags' => [
 574                  'buttons' => [
 575                      'mergetags',
 576                  ],
 577                  'menuitems' => [
 578                      'mergetags',
 579                  ],
 580              ],
 581              'autocorrect' => [
 582                  'menuitems' => [
 583                      'autocorrect',
 584                      'capitalization',
 585                  ],
 586              ],
 587          ];
 588      }
 589  }