Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

   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   * Defines classes used for plugin info.
  19   *
  20   * @package    core
  21   * @copyright  2011 David Mudrak <david@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace core\plugininfo;
  25  
  26  use coding_exception;
  27  use core_component;
  28  use core_plugin_manager;
  29  use moodle_url;
  30  
  31  /**
  32   * Base class providing access to the information about a plugin
  33   *
  34   * @property-read string component the component name, type_name
  35   */
  36  abstract class base {
  37  
  38      /** @var string the plugintype name, eg. mod, auth or workshopform */
  39      public $type;
  40      /** @var string full path to the location of all the plugins of this type */
  41      public $typerootdir;
  42      /** @var string the plugin name, eg. assignment, ldap */
  43      public $name;
  44      /** @var string the localized plugin name */
  45      public $displayname;
  46      /** @var string the plugin source, one of core_plugin_manager::PLUGIN_SOURCE_xxx constants */
  47      public $source;
  48      /** @var string fullpath to the location of this plugin */
  49      public $rootdir;
  50      /** @var int|string the version of the plugin's source code */
  51      public $versiondisk;
  52      /** @var int|string the version of the installed plugin */
  53      public $versiondb;
  54      /** @var int|float|string required version of Moodle core  */
  55      public $versionrequires;
  56      /** @var array explicitly supported branches of Moodle core  */
  57      public $pluginsupported;
  58      /** @var int first incompatible branch of Moodle core  */
  59      public $pluginincompatible;
  60      /** @var mixed human-readable release information */
  61      public $release;
  62      /** @var array other plugins that this one depends on, lazy-loaded by {@link get_other_required_plugins()} */
  63      public $dependencies;
  64      /** @var int number of instances of the plugin - not supported yet */
  65      public $instances;
  66      /** @var int order of the plugin among other plugins of the same type - not supported yet */
  67      public $sortorder;
  68      /** @var core_plugin_manager the plugin manager this plugin info is part of */
  69      public $pluginman;
  70  
  71      /** @var array|null array of {@link \core\update\info} for this plugin */
  72      protected $availableupdates;
  73  
  74      /** @var int Move a plugin up in the plugin order */
  75      public const MOVE_UP = -1;
  76  
  77      /** @var int Move a plugin down in the plugin order */
  78      public const MOVE_DOWN = 1;
  79  
  80      /** @var array hold $plugin->supported in version.php */
  81      public $supported;
  82  
  83      /** @var int hold $plugin->incompatible in version.php  */
  84      public $incompatible;
  85  
  86      /** @var string Name of the plugin */
  87      public $component = '';
  88  
  89      /**
  90       * Whether this plugintype supports its plugins being disabled.
  91       *
  92       * @return bool
  93       */
  94      public static function plugintype_supports_disabling(): bool {
  95          return false;
  96      }
  97  
  98      /**
  99       * Finds all enabled plugins, the result may include missing plugins.
 100       * @return array|null of enabled plugins $pluginname=>$pluginname, null means unknown
 101       */
 102      public static function get_enabled_plugins() {
 103          return null;
 104      }
 105  
 106      /**
 107       * Enable or disable a plugin.
 108       * When possible, the change will be stored into the config_log table, to let admins check when/who has modified it.
 109       *
 110       * @param string $pluginname The plugin name to enable/disable.
 111       * @param int $enabled Whether the pluginname should be enabled (1) or not (0). This is an integer because some plugins, such
 112       * as filters or repositories, might support more statuses than just enabled/disabled.
 113       *
 114       * @return bool Whether $pluginname has been updated or not.
 115       */
 116      public static function enable_plugin(string $pluginname, int $enabled): bool {
 117          return false;
 118      }
 119  
 120      /**
 121       * Returns current status for a pluginname.
 122       *
 123       * @param string $pluginname The plugin name to check.
 124       * @return int The current status (enabled, disabled...) of $pluginname.
 125       */
 126      public static function get_enabled_plugin(string $pluginname): int {
 127          $enabledplugins = static::get_enabled_plugins();
 128          $value = $enabledplugins && array_key_exists($pluginname, $enabledplugins);
 129          return (int) $value;
 130      }
 131  
 132      /**
 133       * Gathers and returns the information about all plugins of the given type,
 134       * either on disk or previously installed.
 135       *
 136       * This is supposed to be used exclusively by the plugin manager when it is
 137       * populating its tree of plugins.
 138       *
 139       * @param string $type the name of the plugintype, eg. mod, auth or workshopform
 140       * @param string $typerootdir full path to the location of the plugin dir
 141       * @param string $typeclass the name of the actually called class
 142       * @param core_plugin_manager $pluginman the plugin manager calling this method
 143       * @return array of plugintype classes, indexed by the plugin name
 144       */
 145      public static function get_plugins($type, $typerootdir, $typeclass, $pluginman) {
 146          // Get the information about plugins at the disk.
 147          $plugins = core_component::get_plugin_list($type);
 148          $return = array();
 149          foreach ($plugins as $pluginname => $pluginrootdir) {
 150              $return[$pluginname] = self::make_plugin_instance($type, $typerootdir,
 151                  $pluginname, $pluginrootdir, $typeclass, $pluginman);
 152          }
 153  
 154          // Fetch missing incorrectly uninstalled plugins.
 155          $plugins = $pluginman->get_installed_plugins($type);
 156  
 157          foreach ($plugins as $name => $version) {
 158              if (isset($return[$name])) {
 159                  continue;
 160              }
 161              $plugin              = new $typeclass();
 162              $plugin->type        = $type;
 163              $plugin->typerootdir = $typerootdir;
 164              $plugin->name        = $name;
 165              $plugin->component   = $plugin->type.'_'.$plugin->name;
 166              $plugin->rootdir     = null;
 167              $plugin->displayname = $name;
 168              $plugin->versiondb   = $version;
 169              $plugin->pluginman   = $pluginman;
 170              $plugin->init_is_standard();
 171  
 172              $return[$name] = $plugin;
 173          }
 174  
 175          return $return;
 176      }
 177  
 178      /**
 179       * Makes a new instance of the plugininfo class
 180       *
 181       * @param string $type the plugin type, eg. 'mod'
 182       * @param string $typerootdir full path to the location of all the plugins of this type
 183       * @param string $name the plugin name, eg. 'workshop'
 184       * @param string $namerootdir full path to the location of the plugin
 185       * @param string $typeclass the name of class that holds the info about the plugin
 186       * @param core_plugin_manager $pluginman the plugin manager of the new instance
 187       * @return base the instance of $typeclass
 188       */
 189      protected static function make_plugin_instance($type, $typerootdir, $name, $namerootdir, $typeclass, $pluginman) {
 190          $plugin              = new $typeclass();
 191          $plugin->type        = $type;
 192          $plugin->typerootdir = $typerootdir;
 193          $plugin->name        = $name;
 194          $plugin->rootdir     = $namerootdir;
 195          $plugin->pluginman   = $pluginman;
 196          $plugin->component   = $plugin->type.'_'.$plugin->name;
 197  
 198          $plugin->init_display_name();
 199          $plugin->load_disk_version();
 200          $plugin->load_db_version();
 201          $plugin->init_is_standard();
 202  
 203          return $plugin;
 204      }
 205  
 206      /**
 207       * Is this plugin already installed and updated?
 208       * @return bool true if plugin installed and upgraded.
 209       */
 210      public function is_installed_and_upgraded() {
 211          if (!$this->rootdir) {
 212              return false;
 213          }
 214          if ($this->versiondb === null and $this->versiondisk === null) {
 215              // There is no version.php or version info inside it.
 216              return false;
 217          }
 218  
 219          return ((float)$this->versiondb === (float)$this->versiondisk);
 220      }
 221  
 222      /**
 223       * Sets {@link $displayname} property to a localized name of the plugin
 224       */
 225      public function init_display_name() {
 226          if (!get_string_manager()->string_exists('pluginname', $this->component)) {
 227              $this->displayname = '[pluginname,' . $this->component . ']';
 228          } else {
 229              $this->displayname = get_string('pluginname', $this->component);
 230          }
 231      }
 232  
 233      /**
 234       * Magic method getter, redirects to read only values.
 235       *
 236       * @param string $name
 237       * @return mixed
 238       */
 239      public function __get($name) {
 240          switch ($name) {
 241              case 'component': return $this->type . '_' . $this->name;
 242  
 243              default:
 244                  debugging('Invalid plugin property accessed! '.$name);
 245                  return null;
 246          }
 247      }
 248  
 249      /**
 250       * Return the full path name of a file within the plugin.
 251       *
 252       * No check is made to see if the file exists.
 253       *
 254       * @param string $relativepath e.g. 'version.php'.
 255       * @return string e.g. $CFG->dirroot . '/mod/quiz/version.php'.
 256       */
 257      public function full_path($relativepath) {
 258          if (empty($this->rootdir)) {
 259              return '';
 260          }
 261          return $this->rootdir . '/' . $relativepath;
 262      }
 263  
 264      /**
 265       * Sets {@link $versiondisk} property to a numerical value representing the
 266       * version of the plugin's source code.
 267       *
 268       * If the value is null after calling this method, either the plugin
 269       * does not use versioning (typically does not have any database
 270       * data) or is missing from disk.
 271       */
 272      public function load_disk_version() {
 273          $versions = $this->pluginman->get_present_plugins($this->type);
 274  
 275          $this->versiondisk = null;
 276          $this->versionrequires = null;
 277          $this->pluginsupported = null;
 278          $this->pluginincompatible = null;
 279          $this->dependencies = array();
 280  
 281          if (!isset($versions[$this->name])) {
 282              return;
 283          }
 284  
 285          $plugin = $versions[$this->name];
 286  
 287          if (isset($plugin->version)) {
 288              $this->versiondisk = $plugin->version;
 289          }
 290          if (isset($plugin->requires)) {
 291              $this->versionrequires = $plugin->requires;
 292          }
 293          if (isset($plugin->release)) {
 294              $this->release = $plugin->release;
 295          }
 296          if (isset($plugin->dependencies)) {
 297              $this->dependencies = $plugin->dependencies;
 298          }
 299  
 300          // Check that supports and incompatible are wellformed, exception otherwise.
 301          if (isset($plugin->supported)) {
 302              // Checks for structure of supported.
 303              $isint = (is_int($plugin->supported[0]) && is_int($plugin->supported[1]));
 304              $isrange = ($plugin->supported[0] <= $plugin->supported[1] && count($plugin->supported) == 2);
 305  
 306              if (is_array($plugin->supported) && $isint && $isrange) {
 307                  $this->pluginsupported = $plugin->supported;
 308              } else {
 309                  throw new coding_exception('Incorrect syntax in plugin supported declaration in '."$this->name");
 310              }
 311          }
 312  
 313          if (isset($plugin->incompatible) && $plugin->incompatible !== null) {
 314              if (((is_string($plugin->incompatible) && ctype_digit($plugin->incompatible)) || is_int($plugin->incompatible))
 315                      && (int) $plugin->incompatible > 0) {
 316                  $this->pluginincompatible = intval($plugin->incompatible);
 317              } else {
 318                  throw new coding_exception('Incorrect syntax in plugin incompatible declaration in '."$this->name");
 319              }
 320          }
 321  
 322      }
 323  
 324      /**
 325       * Get the list of other plugins that this plugin requires to be installed.
 326       *
 327       * @return array with keys the frankenstyle plugin name, and values either
 328       *      a version string (like '2011101700') or the constant ANY_VERSION.
 329       */
 330      public function get_other_required_plugins() {
 331          if (is_null($this->dependencies)) {
 332              $this->load_disk_version();
 333          }
 334          return $this->dependencies;
 335      }
 336  
 337      /**
 338       * Is this is a subplugin?
 339       *
 340       * @return boolean
 341       */
 342      public function is_subplugin() {
 343          return ($this->get_parent_plugin() !== false);
 344      }
 345  
 346      /**
 347       * If I am a subplugin, return the name of my parent plugin.
 348       *
 349       * @return string|bool false if not a subplugin, name of the parent otherwise
 350       */
 351      public function get_parent_plugin() {
 352          return $this->pluginman->get_parent_of_subplugin($this->type);
 353      }
 354  
 355      /**
 356       * Sets {@link $versiondb} property to a numerical value representing the
 357       * currently installed version of the plugin.
 358       *
 359       * If the value is null after calling this method, either the plugin
 360       * does not use versioning (typically does not have any database
 361       * data) or has not been installed yet.
 362       */
 363      public function load_db_version() {
 364          $versions = $this->pluginman->get_installed_plugins($this->type);
 365  
 366          if (isset($versions[$this->name])) {
 367              $this->versiondb = $versions[$this->name];
 368          } else {
 369              $this->versiondb = null;
 370          }
 371      }
 372  
 373      /**
 374       * Sets {@link $source} property to one of core_plugin_manager::PLUGIN_SOURCE_xxx
 375       * constants.
 376       *
 377       * If the property's value is null after calling this method, then
 378       * the type of the plugin has not been recognized and you should throw
 379       * an exception.
 380       */
 381      public function init_is_standard() {
 382  
 383          $pluginman = $this->pluginman;
 384          $standard = $pluginman::standard_plugins_list($this->type);
 385  
 386          if ($standard !== false) {
 387              $standard = array_flip($standard);
 388              if (isset($standard[$this->name])) {
 389                  $this->source = core_plugin_manager::PLUGIN_SOURCE_STANDARD;
 390              } else if (!is_null($this->versiondb) and is_null($this->versiondisk)
 391                  and $pluginman::is_deleted_standard_plugin($this->type, $this->name)) {
 392                  $this->source = core_plugin_manager::PLUGIN_SOURCE_STANDARD; // To be deleted.
 393              } else {
 394                  $this->source = core_plugin_manager::PLUGIN_SOURCE_EXTENSION;
 395              }
 396          }
 397      }
 398  
 399      /**
 400       * Returns true if the plugin is shipped with the official distribution
 401       * of the current Moodle version, false otherwise.
 402       *
 403       * @return bool
 404       */
 405      public function is_standard() {
 406          return $this->source === core_plugin_manager::PLUGIN_SOURCE_STANDARD;
 407      }
 408  
 409      /**
 410       * Returns true if the the given Moodle version is enough to run this plugin
 411       *
 412       * @param string|int|double $moodleversion
 413       * @return bool
 414       */
 415      public function is_core_dependency_satisfied($moodleversion) {
 416  
 417          if (empty($this->versionrequires)) {
 418              return true;
 419  
 420          } else {
 421              return (double)$this->versionrequires <= (double)$moodleversion;
 422          }
 423      }
 424  
 425      /**
 426       * Returns true if the the given moodle branch is not stated incompatible with the plugin
 427       *
 428       * @param int $branch the moodle branch number
 429       * @return bool true if not incompatible with moodle branch
 430       */
 431      public function is_core_compatible_satisfied(int $branch) : bool {
 432          if (!empty($this->pluginincompatible) && ($branch >= $this->pluginincompatible)) {
 433              return false;
 434          } else {
 435              return true;
 436          }
 437      }
 438  
 439      /**
 440       * Returns the status of the plugin
 441       *
 442       * @return string one of core_plugin_manager::PLUGIN_STATUS_xxx constants
 443       */
 444      public function get_status() {
 445  
 446          $pluginman = $this->pluginman;
 447  
 448          if (is_null($this->versiondb) and is_null($this->versiondisk)) {
 449              return core_plugin_manager::PLUGIN_STATUS_NODB;
 450  
 451          } else if (is_null($this->versiondb) and !is_null($this->versiondisk)) {
 452              return core_plugin_manager::PLUGIN_STATUS_NEW;
 453  
 454          } else if (!is_null($this->versiondb) and is_null($this->versiondisk)) {
 455              if ($pluginman::is_deleted_standard_plugin($this->type, $this->name)) {
 456                  return core_plugin_manager::PLUGIN_STATUS_DELETE;
 457              } else {
 458                  return core_plugin_manager::PLUGIN_STATUS_MISSING;
 459              }
 460  
 461          } else if ((float)$this->versiondb === (float)$this->versiondisk) {
 462              // Note: the float comparison should work fine here
 463              //       because there are no arithmetic operations with the numbers.
 464              return core_plugin_manager::PLUGIN_STATUS_UPTODATE;
 465  
 466          } else if ($this->versiondb < $this->versiondisk) {
 467              return core_plugin_manager::PLUGIN_STATUS_UPGRADE;
 468  
 469          } else if ($this->versiondb > $this->versiondisk) {
 470              return core_plugin_manager::PLUGIN_STATUS_DOWNGRADE;
 471  
 472          } else {
 473              // $version = pi(); and similar funny jokes - hopefully Donald E. Knuth will never contribute to Moodle ;-)
 474              throw new coding_exception('Unable to determine plugin state, check the plugin versions');
 475          }
 476      }
 477  
 478      /**
 479       * Returns the information about plugin availability
 480       *
 481       * True means that the plugin is enabled. False means that the plugin is
 482       * disabled. Null means that the information is not available, or the
 483       * plugin does not support configurable availability or the availability
 484       * can not be changed.
 485       *
 486       * @return null|bool
 487       */
 488      public function is_enabled() {
 489          if (!$this->rootdir) {
 490              // Plugin missing.
 491              return false;
 492          }
 493  
 494          $enabled = $this->pluginman->get_enabled_plugins($this->type);
 495  
 496          if (!is_array($enabled)) {
 497              return null;
 498          }
 499  
 500          return isset($enabled[$this->name]);
 501      }
 502  
 503      /**
 504       * If there are updates for this plugin available, returns them.
 505       *
 506       * Returns array of {@link \core\update\info} objects, if some update
 507       * is available. Returns null if there is no update available or if the update
 508       * availability is unknown.
 509       *
 510       * Populates the property {@link $availableupdates} on first call (lazy
 511       * loading).
 512       *
 513       * @return array|null
 514       */
 515      public function available_updates() {
 516  
 517          if ($this->availableupdates === null) {
 518              // Lazy load the information about available updates.
 519              $this->availableupdates = $this->pluginman->load_available_updates_for_plugin($this->component);
 520          }
 521  
 522          if (empty($this->availableupdates) or !is_array($this->availableupdates)) {
 523              $this->availableupdates = array();
 524              return null;
 525          }
 526  
 527          $updates = array();
 528  
 529          foreach ($this->availableupdates as $availableupdate) {
 530              if ($availableupdate->version > $this->versiondisk) {
 531                  $updates[] = $availableupdate;
 532              }
 533          }
 534  
 535          if (empty($updates)) {
 536              return null;
 537          }
 538  
 539          return $updates;
 540      }
 541  
 542      /**
 543       * Returns the node name used in admin settings menu for this plugin settings (if applicable)
 544       *
 545       * @return null|string node name or null if plugin does not create settings node (default)
 546       */
 547      public function get_settings_section_name() {
 548          return null;
 549      }
 550  
 551      /**
 552       * Returns the URL of the plugin settings screen
 553       *
 554       * Null value means that the plugin either does not have the settings screen
 555       * or its location is not available via this library.
 556       *
 557       * @return null|moodle_url
 558       */
 559      public function get_settings_url(): ?moodle_url {
 560          $section = $this->get_settings_section_name();
 561          if ($section === null) {
 562              return null;
 563          }
 564  
 565          $settings = admin_get_root()->locate($section);
 566          if ($settings && $settings instanceof \core_admin\local\settings\linkable_settings_page) {
 567              return $settings->get_settings_page_url();
 568          }
 569  
 570          return null;
 571      }
 572  
 573      /**
 574       * Loads plugin settings to the settings tree
 575       *
 576       * This function usually includes settings.php file in plugins folder.
 577       * Alternatively it can create a link to some settings page (instance of admin_externalpage)
 578       *
 579       * @param \part_of_admin_tree $adminroot
 580       * @param string $parentnodename
 581       * @param bool $hassiteconfig whether the current user has moodle/site:config capability
 582       */
 583      public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig) {
 584      }
 585  
 586      /**
 587       * Should there be a way to uninstall the plugin via the administration UI.
 588       *
 589       * By default uninstallation is not allowed, plugin developers must enable it explicitly!
 590       *
 591       * @return bool
 592       */
 593      public function is_uninstall_allowed() {
 594          return false;
 595      }
 596  
 597      /**
 598       * Optional extra warning before uninstallation, for example number of uses in courses.
 599       *
 600       * @return string
 601       */
 602      public function get_uninstall_extra_warning() {
 603          return '';
 604      }
 605  
 606      /**
 607       * Pre-uninstall hook.
 608       *
 609       * This is intended for disabling of plugin, some DB table purging, etc.
 610       *
 611       * NOTE: to be called from uninstall_plugin() only.
 612       * @private
 613       */
 614      public function uninstall_cleanup() {
 615          // Override when extending class,
 616          // do not forget to call parent::pre_uninstall_cleanup() at the end.
 617      }
 618  
 619      /**
 620       * Returns relative directory of the plugin with heading '/'
 621       *
 622       * @return string
 623       */
 624      public function get_dir() {
 625          global $CFG;
 626  
 627          if (!isset($this->rootdir)) {
 628              return '';
 629          }
 630  
 631          return substr($this->rootdir, strlen($CFG->dirroot));
 632      }
 633  
 634      /**
 635       * Hook method to implement certain steps when uninstalling the plugin.
 636       *
 637       * This hook is called by {@link core_plugin_manager::uninstall_plugin()} so
 638       * it is basically usable only for those plugin types that use the default
 639       * uninstall tool provided by {@link self::get_default_uninstall_url()}.
 640       *
 641       * @param \progress_trace $progress traces the process
 642       * @return bool true on success, false on failure
 643       */
 644      public function uninstall(\progress_trace $progress) {
 645          return true;
 646      }
 647  
 648      /**
 649       * Where should we return after plugin of this type is uninstalled?
 650       * @param string $return
 651       * @return moodle_url
 652       */
 653      public function get_return_url_after_uninstall($return) {
 654          if ($return === 'manage') {
 655              if ($url = $this->get_manage_url()) {
 656                  return $url;
 657              }
 658          }
 659          return new moodle_url('/admin/plugins.php#plugin_type_cell_'.$this->type);
 660      }
 661  
 662      /**
 663       * Return URL used for management of plugins of this type.
 664       * @return moodle_url
 665       */
 666      public static function get_manage_url() {
 667          return null;
 668      }
 669  
 670      /**
 671       * Returns URL to a script that handles common plugin uninstall procedure.
 672       *
 673       * This URL is intended for all plugin uninstallations.
 674       *
 675       * @param string $return either 'overview' or 'manage'
 676       * @return moodle_url
 677       */
 678      public final function get_default_uninstall_url($return = 'overview') {
 679          return new moodle_url('/admin/plugins.php', array(
 680              'uninstall' => $this->component,
 681              'confirm' => 0,
 682              'return' => $return,
 683          ));
 684      }
 685  
 686      /**
 687       * Whether this plugintype supports ordering of plugins using native functionality.
 688       *
 689       * Please note that plugintypes which pre-date this native functionality may still support ordering
 690       * but will not use the built-in functionality.
 691       *
 692       * @return bool
 693       */
 694      public static function plugintype_supports_ordering(): bool {
 695          return false;
 696      }
 697  
 698      /**
 699       * Finds all enabled plugins, the result may include missing plugins.
 700       *
 701       * @param bool $enabledonly Show all plugins, or only those which are enabled
 702       * @return array|null of sorted plugins $pluginname => $pluginname, null means unknown
 703       */
 704      public static function get_sorted_plugins(bool $enabledonly = false): ?array {
 705          return null;
 706      }
 707  
 708      /**
 709       * Change the order of the plugin relative to other plugins in the plugintype.
 710       *
 711       * When possible, the change will be stored into the config_log table, to let admins check when/who has modified it.
 712       *
 713       * @param string $pluginname The plugin name to enable/disable.
 714       * @param int $direction The direction to move the plugin. Negative numbers mean up, Positive mean down.
 715       * @return bool Whether $pluginname has been updated or not.
 716       */
 717      public static function change_plugin_order(string $pluginname, int $direction): bool {
 718          return false;
 719      }
 720  }