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   * @package   moodlecore
  19   * @copyright 2010 Sam Hemelryk
  20   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  21   */
  22  
  23  /**
  24   * Generic abstract dependency class
  25   *
  26   * @copyright 2010 Sam Hemelryk
  27   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  abstract class setting_dependency {
  30  
  31      /**
  32       * Used to define the type of a dependency.
  33       *
  34       * Note with these that checked and true, and not checked and false are equal.
  35       * This is because the terminology differs but the resulting action is the same.
  36       * Reduces code!
  37       */
  38      const DISABLED_VALUE = 0;
  39      const DISABLED_NOT_VALUE = 1;
  40      const DISABLED_TRUE = 2;
  41      const DISABLED_FALSE = 3;
  42      const DISABLED_CHECKED = 4;
  43      const DISABLED_NOT_CHECKED = 5;
  44      const DISABLED_EMPTY = 6;
  45      const DISABLED_NOT_EMPTY = 7;
  46  
  47      /**
  48       * The parent setting (primary)
  49       * @var base_setting
  50       */
  51      protected $setting;
  52      /**
  53       * The dependent setting (secondary)
  54       * @var base_setting
  55       */
  56      protected $dependentsetting;
  57      /**
  58       * The default setting
  59       * @var mixed
  60       */
  61      protected $defaultvalue;
  62      /**
  63       * The last value the dependent setting had
  64       * @var mixed
  65       */
  66      protected $lastvalue;
  67      /**
  68       * Creates the dependency object
  69       * @param base_setting $setting The parent setting or the primary setting if you prefer
  70       * @param base_setting $dependentsetting The dependent setting
  71       * @param mixed $defaultvalue The default value to assign if the dependency is unmet
  72       */
  73      public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
  74          $this->setting = $setting;
  75          $this->dependentsetting = $dependentsetting;
  76          $this->defaultvalue = $defaultvalue;
  77          $this->lastvalue = $dependentsetting->get_value();
  78      }
  79  
  80      /**
  81       * Destroy all circular references. It helps PHP 5.2 a lot!
  82       */
  83      public function destroy() {
  84          // No need to destroy anything recursively here, direct reset.
  85          $this->setting = null;
  86          $this->dependentsetting = null;
  87      }
  88  
  89      /**
  90       * Processes a change is setting called by the primary setting
  91       * @param int $changetype
  92       * @param mixed $oldvalue
  93       * @return bool
  94       */
  95      final public function process_change($changetype, $oldvalue) {
  96          // Check the type of change requested.
  97          switch ($changetype) {
  98              // Process a status change.
  99              case base_setting::CHANGED_STATUS:
 100                  return $this->process_status_change($oldvalue);
 101              // Process a visibility change.
 102              case base_setting::CHANGED_VISIBILITY:
 103                  return $this->process_visibility_change($oldvalue);
 104              // Process a value change.
 105              case base_setting::CHANGED_VALUE:
 106                  return $this->process_value_change($oldvalue);
 107          }
 108          // Throw an exception if we get this far.
 109          throw new backup_ui_exception('unknownchangetype');
 110      }
 111      /**
 112       * Processes a visibility change
 113       * @param bool $oldvisibility
 114       * @return bool
 115       */
 116      protected function process_visibility_change($oldvisibility) {
 117          // Store the current dependent settings visibility for comparison.
 118          $prevalue = $this->dependentsetting->get_visibility();
 119          // Set it regardless of whether we need to.
 120          $this->dependentsetting->set_visibility($this->setting->get_visibility());
 121          // Return true if it changed.
 122          return ($prevalue != $this->dependentsetting->get_visibility());
 123      }
 124      /**
 125       * All dependencies must define how they would like to deal with a status change
 126       * @param int $oldstatus
 127       */
 128      abstract protected function process_status_change($oldstatus);
 129      /**
 130       * All dependencies must define how they would like to process a value change
 131       */
 132      abstract protected function process_value_change($oldvalue);
 133      /**
 134       * Gets the primary setting
 135       * @return backup_setting
 136       */
 137      public function get_setting() {
 138          return $this->setting;
 139      }
 140      /**
 141       * Gets the dependent setting
 142       * @return backup_setting
 143       */
 144      public function get_dependent_setting() {
 145          return $this->dependentsetting;
 146      }
 147      /**
 148       * This function enforces the dependency
 149       */
 150      abstract public function enforce();
 151      /**
 152       * Returns an array of properties suitable to be used to define a moodleforms
 153       * disabled command
 154       * @return array
 155       */
 156      abstract public function get_moodleform_properties();
 157      /**
 158       * Returns true if the dependent setting is locked by this setting_dependency.
 159       * @return bool
 160       */
 161      abstract public function is_locked();
 162  }
 163  
 164  /**
 165   * A dependency that disables the secondary setting if the primary setting is
 166   * equal to the provided value
 167   *
 168   * @copyright 2010 Sam Hemelryk
 169   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 170   */
 171  class setting_dependency_disabledif_equals extends setting_dependency {
 172      /**
 173       * The value to compare to
 174       * @var mixed
 175       */
 176      protected $value;
 177      /**
 178       * Creates the dependency
 179       *
 180       * @param base_setting $setting
 181       * @param base_setting $dependentsetting
 182       * @param mixed $value
 183       * @param mixed $defaultvalue
 184       */
 185      public function __construct(base_setting $setting, base_setting $dependentsetting, $value, $defaultvalue = false) {
 186          parent::__construct($setting, $dependentsetting, $defaultvalue);
 187          $this->value = ($value) ? (string)$value : 0;
 188      }
 189      /**
 190       * Returns true if the dependent setting is locked by this setting_dependency.
 191       * @return bool
 192       */
 193      public function is_locked() {
 194          // If the setting is locked or the dependent setting should be locked then return true.
 195          if ($this->setting->get_status() !== base_setting::NOT_LOCKED ||
 196                  $this->evaluate_disabled_condition($this->setting->get_value())) {
 197              return true;
 198          }
 199          // Else the dependent setting is not locked by this setting_dependency.
 200          return false;
 201      }
 202      /**
 203       * Processes a value change in the primary setting
 204       * @param mixed $oldvalue
 205       * @return bool
 206       */
 207      protected function process_value_change($oldvalue) {
 208          if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_PERMISSION ||
 209                  $this->dependentsetting->get_status() == base_setting::LOCKED_BY_CONFIG) {
 210              // When setting is locked by permission or config do not apply dependencies.
 211              return false;
 212          }
 213          $prevalue = $this->dependentsetting->get_value();
 214          // If the setting is the desired value enact the dependency.
 215          $settingvalue = $this->setting->get_value();
 216          if ($this->evaluate_disabled_condition($settingvalue)) {
 217              // The dependent setting needs to be locked by hierachy and set to the
 218              // default value.
 219              $this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
 220  
 221              // For checkboxes the default value is false, but when the setting is
 222              // locked, the value should inherit from the parent setting.
 223              if ($this->defaultvalue === false) {
 224                  $this->dependentsetting->set_value($settingvalue);
 225              } else {
 226                  $this->dependentsetting->set_value($this->defaultvalue);
 227              }
 228          } else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
 229              // We can unlock the dependent setting.
 230              $this->dependentsetting->set_status(base_setting::NOT_LOCKED);
 231          }
 232          // Return true if the value has changed for the dependent setting.
 233          return ($prevalue != $this->dependentsetting->get_value());
 234      }
 235      /**
 236       * Processes a status change in the primary setting
 237       * @param mixed $oldstatus
 238       * @return bool
 239       */
 240      protected function process_status_change($oldstatus) {
 241          // Store the dependent status.
 242          $prevalue = $this->dependentsetting->get_status();
 243          // Store the current status.
 244          $currentstatus = $this->setting->get_status();
 245          if ($currentstatus == base_setting::NOT_LOCKED) {
 246              if ($prevalue == base_setting::LOCKED_BY_HIERARCHY &&
 247                      !$this->evaluate_disabled_condition($this->setting->get_value())) {
 248                  // Dependency has changes, is not fine, unlock the dependent setting.
 249                  $this->dependentsetting->set_status(base_setting::NOT_LOCKED);
 250              }
 251          } else {
 252              // Make sure the dependent setting is also locked, in this case by hierarchy.
 253              $this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
 254          }
 255          // Return true if the dependent setting has changed.
 256          return ($prevalue != $this->dependentsetting->get_status());
 257      }
 258      /**
 259       * Enforces the dependency if required.
 260       * @return bool True if there were changes
 261       */
 262      public function enforce() {
 263          // This will be set to true if ANYTHING changes.
 264          $changes = false;
 265          // First process any value changes.
 266          if ($this->process_value_change($this->setting->get_value())) {
 267              $changes = true;
 268          }
 269          // Second process any status changes.
 270          if ($this->process_status_change($this->setting->get_status())) {
 271              $changes = true;
 272          }
 273          // Finally process visibility changes.
 274          if ($this->process_visibility_change($this->setting->get_visibility())) {
 275              $changes = true;
 276          }
 277          return $changes;
 278      }
 279      /**
 280       * Returns an array of properties suitable to be used to define a moodleforms
 281       * disabled command
 282       * @return array
 283       */
 284      public function get_moodleform_properties() {
 285          return array(
 286              'setting' => $this->dependentsetting->get_ui_name(),
 287              'dependenton' => $this->setting->get_ui_name(),
 288              'condition' => 'eq',
 289              'value' => $this->value
 290          );
 291      }
 292  
 293      /**
 294       * Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
 295       * This function should be abstract, but there will probably be existing sub-classes so we must provide a default
 296       * implementation.
 297       * @param mixed $value The value of the parent setting.
 298       * @return bool
 299       */
 300      protected function evaluate_disabled_condition($value) {
 301          return $value == $this->value;
 302      }
 303  }
 304  
 305  /**
 306   * A dependency that disables the secondary setting if the primary setting is
 307   * not equal to the provided value
 308   *
 309   * @copyright 2011 Darko Miletic <dmiletic@moodlerooms.com>
 310   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 311   */
 312  class setting_dependency_disabledif_not_equals extends setting_dependency_disabledif_equals {
 313  
 314      /**
 315       * Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
 316       * @param mixed $value The value of the parent setting.
 317       * @return bool
 318       */
 319      protected function evaluate_disabled_condition($value) {
 320          return $value != $this->value;
 321      }
 322  
 323      /**
 324       * Returns an array of properties suitable to be used to define a moodleforms
 325       * disabled command
 326       * @return array
 327       */
 328      public function get_moodleform_properties() {
 329          return array(
 330                  'setting' => $this->dependentsetting->get_ui_name(),
 331                  'dependenton' => $this->setting->get_ui_name(),
 332                  'condition' => 'notequal',
 333                  'value' => $this->value
 334          );
 335      }
 336  }
 337  
 338  /**
 339   * Disable if a value is in a list.
 340   */
 341  class setting_dependency_disabledif_in_array extends setting_dependency_disabledif_equals {
 342  
 343      /**
 344       * Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
 345       * @param mixed $value The value of the parent setting.
 346       * @return bool
 347       */
 348      protected function evaluate_disabled_condition($value) {
 349          return in_array($value, $this->value);
 350      }
 351  
 352      /**
 353       * Returns an array of properties suitable to be used to define a moodleforms
 354       * disabled command
 355       * @return array
 356       */
 357      public function get_moodleform_properties() {
 358          return array(
 359              'setting' => $this->dependentsetting->get_ui_name(),
 360              'dependenton' => $this->setting->get_ui_name(),
 361              'condition' => 'eq',
 362              'value' => $this->value
 363          );
 364      }
 365  }
 366  
 367  /**
 368   * This class is here for backwards compatibility (terrible name).
 369   */
 370  class setting_dependency_disabledif_equals2 extends setting_dependency_disabledif_in_array {
 371  }
 372  
 373  /**
 374   * A dependency that disables the secondary element if the primary element is
 375   * true or checked
 376   *
 377   * @copyright 2010 Sam Hemelryk
 378   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 379   */
 380  class setting_dependency_disabledif_checked extends setting_dependency_disabledif_equals {
 381      public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
 382          parent::__construct($setting, $dependentsetting, true, $defaultvalue);
 383          $this->value = true;
 384      }
 385      /**
 386       * Returns an array of properties suitable to be used to define a moodleforms
 387       * disabled command
 388       * @return array
 389       */
 390      public function get_moodleform_properties() {
 391          return array(
 392              'setting' => $this->dependentsetting->get_ui_name(),
 393              'dependenton' => $this->setting->get_ui_name(),
 394              'condition' => 'checked'
 395          );
 396      }
 397  }
 398  
 399  /**
 400   * A dependency that disables the secondary element if the primary element is
 401   * false or not checked
 402   *
 403   * @copyright 2010 Sam Hemelryk
 404   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 405   */
 406  class setting_dependency_disabledif_not_checked extends setting_dependency_disabledif_equals {
 407      public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
 408          parent::__construct($setting, $dependentsetting, false, $defaultvalue);
 409          $this->value = false;
 410      }
 411      /**
 412       * Returns an array of properties suitable to be used to define a moodleforms
 413       * disabled command
 414       * @return array
 415       */
 416      public function get_moodleform_properties() {
 417          return array(
 418              'setting' => $this->dependentsetting->get_ui_name(),
 419              'dependenton' => $this->setting->get_ui_name(),
 420              'condition' => 'notchecked'
 421          );
 422      }
 423  }
 424  
 425  /**
 426   * A dependency that disables the secondary setting if the value of the primary setting
 427   * is not empty.
 428   *
 429   * @copyright 2010 Sam Hemelryk
 430   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 431   */
 432  class setting_dependency_disabledif_not_empty extends setting_dependency_disabledif_equals {
 433      public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
 434          parent::__construct($setting, $dependentsetting, false, $defaultvalue);
 435          $this->value = false;
 436      }
 437  
 438      /**
 439       * Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
 440       * @param mixed $value The value of the parent setting.
 441       * @return bool
 442       */
 443      protected function evaluate_disabled_condition($value) {
 444          return !empty($value);
 445      }
 446  
 447      /**
 448       * Returns an array of properties suitable to be used to define a moodleforms
 449       * disabled command
 450       * @return array
 451       */
 452      public function get_moodleform_properties() {
 453          return array(
 454              'setting' => $this->dependentsetting->get_ui_name(),
 455              'dependenton' => $this->setting->get_ui_name(),
 456              'condition' => 'notequal',
 457              'value' => ''
 458          );
 459      }
 460  }
 461  
 462  /**
 463   * A dependency that disables the secondary setting if the value of the primary setting
 464   * is empty.
 465   *
 466   * @copyright 2010 Sam Hemelryk
 467   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 468   */
 469  class setting_dependency_disabledif_empty extends setting_dependency_disabledif_equals {
 470      public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
 471          parent::__construct($setting, $dependentsetting, false, $defaultvalue);
 472          $this->value = false;
 473      }
 474  
 475      /**
 476       * Evaluate the current value of the setting and return true if the dependent setting should be locked or false.
 477       * @param mixed $value The value of the parent setting.
 478       * @return bool
 479       */
 480      protected function evaluate_disabled_condition($value) {
 481          return empty($value);
 482      }
 483  
 484      /**
 485       * Returns an array of properties suitable to be used to define a moodleforms
 486       * disabled command
 487       * @return array
 488       */
 489      public function get_moodleform_properties() {
 490          return array(
 491              'setting' => $this->dependentsetting->get_ui_name(),
 492              'dependenton' => $this->setting->get_ui_name(),
 493              'condition' => 'notequal',
 494              'value' => ''
 495          );
 496      }
 497  }