backup MDL-22511 Fixed unit tests for backup
authorSam Hemelryk <sam@moodle.com>
Tue, 15 Jun 2010 02:15:57 +0000 (02:15 +0000)
committerSam Hemelryk <sam@moodle.com>
Tue, 15 Jun 2010 02:15:57 +0000 (02:15 +0000)
backup/util/settings/backup_setting.class.php
backup/util/settings/base_setting.class.php
backup/util/settings/setting_dependency.class.php
backup/util/settings/simpletest/testsettings.php
backup/util/ui/backup_ui_setting.class.php

index 279afa6..8a86e4c 100644 (file)
@@ -35,12 +35,63 @@ abstract class backup_setting extends base_setting implements checksumable {
     const SECTION_LEVEL  = 9;
     const ACTIVITY_LEVEL = 13;
 
+    /**
+     * One of the above constants
+     * @var {int}
+     */
     protected $level;  // level of the setting
 
+    public function __construct($name, $vtype, $value = null, $visibility = self::VISIBLE, $status = self::NOT_LOCKED) {
+        parent::__construct($name, $vtype, $value, $visibility, $status);
+        // Generate a default ui
+        $this->uisetting = new backup_setting_ui_checkbox($this, $name);
+    }
+
+    /**
+     * Returns the level of the setting
+     * 
+     * @return {int} One of the above constants
+     */
     public function get_level() {
         return $this->level;
     }
 
+    /**
+     * Creates and sets a user interface for this setting given appropriate arguments
+     *
+     * @param int $type
+     * @param string $label
+     * @param array $attributes
+     * @param array $options
+     */
+    public function make_ui($type, $label, array $attributes = null, array $options = null) {
+        $type = $this->validate_ui_type($type);
+        $label = $this->validate_ui_label($label);
+        $this->uisetting = backup_setting_ui::make($this, $type, $label, $attributes, $options);
+        if (is_array($options) || is_object($options)) {
+            $options = (array)$options;
+            switch (get_class($this->uisetting)) {
+                case 'backup_setting_ui_radio' :
+                    // text
+                    if (array_key_exists('text', $options)) {
+                        $this->uisetting->set_text($options['text']);
+                    }
+                case 'backup_setting_ui_checkbox' :
+                    // value
+                    if (array_key_exists('value', $options)) {
+                        $this->uisetting->set_value($options['value']);
+                    }
+                    break;
+                case 'backup_setting_ui_select' :
+                    // options
+                    if (array_key_exists('options', $options)) {
+                        $this->uisetting->set_values($options['options']);
+                    }
+                    break;
+            }
+        }
+    }
+
     public function add_dependency(backup_setting $dependentsetting, $type=setting_dependency::DISABLED_VALUE, $options=array()) {
         // Check the dependency level is >= current level
         if ($dependentsetting->get_level() < $this->level) {
index 375a3d4..38bf40b 100644 (file)
@@ -108,7 +108,7 @@ abstract class base_setting {
         $this->status      = $status;
 
         // Generate a default ui
-        $this->uisetting = new backup_setting_ui_checkbox($this, $name);
+        $this->uisetting = new base_setting_ui($this);
     }
 
     public function get_name() {
@@ -237,52 +237,16 @@ abstract class base_setting {
     /**
      * Sets the user interface for this setting
      *
-     * @param backup_setting_ui $ui
+     * @param base_setting_ui $ui
      */
     public function set_ui(backup_setting_ui $ui) {
         $this->uisetting = $ui;
     }
 
-    /**
-     * Creates and sets a user interface for this setting given appropriate arguments
-     *
-     * @param int $type
-     * @param string $label
-     * @param array $attributes
-     * @param array $options 
-     */
-    public function make_ui($type, $label, array $attributes = null, array $options = null) {
-        $type = $this->validate_ui_type($type);
-        $label = $this->validate_ui_label($label);
-        $this->uisetting = backup_setting_ui::make($this, $type, $label, $attributes, $options);
-        if (is_array($options) || is_object($options)) {
-            $options = (array)$options;
-            switch (get_class($this->uisetting)) {
-                case 'backup_setting_ui_radio' :
-                    // text
-                    if (array_key_exists('text', $options)) {
-                        $this->uisetting->set_text($options['text']);
-                    }
-                case 'backup_setting_ui_checkbox' :
-                    // value
-                    if (array_key_exists('value', $options)) {
-                        $this->uisetting->set_value($options['value']);
-                    }
-                    break;
-                case 'backup_setting_ui_select' :
-                    // options
-                    if (array_key_exists('options', $options)) {
-                        $this->uisetting->set_values($options['options']);
-                    }
-                    break;
-            }
-        }
-    }
-
     /**
      * Gets the user interface for this setting
      *
-     * @return backup_setting_ui
+     * @return base_setting_ui
      */
     public function get_ui() {
         return $this->uisetting;
@@ -386,6 +350,12 @@ abstract class base_setting {
             case setting_dependency::DISABLED_NOT_CHECKED :
                 $dependency = new setting_dependency_disabledif_not_checked($this, $dependentsetting, $options['defaultvalue']);
                 break;
+            case setting_dependency::DISABLED_EMPTY :
+                $dependency = new setting_dependency_disabledif_empty($this, $dependentsetting, $options['defaultvalue']);
+                break;
+            case setting_dependency::DISABLED_NOT_EMPTY :
+                $dependency = new setting_dependency_disabledif_not_empty($this, $dependentsetting, $options['defaultvalue']);
+                break;
         }
         $this->dependencies[$dependentsetting->get_name()] = $dependency;
         $dependency->get_dependent_setting()->register_dependent_dependency($dependency);
@@ -472,6 +442,12 @@ abstract class base_setting {
         if (array_key_exists($this->name, $dependencies) || $obj == $this) {
             return true;
         }
+        // Recurse the dependent settings one by one
+        foreach ($dependencies as $dependency) {
+            if ($dependency->get_dependent_setting()->is_circular_reference($obj)) {
+                return true;
+            }
+        }
         return false;
     }
 
index d69c863..7c612f3 100644 (file)
@@ -42,6 +42,8 @@ abstract class setting_dependency {
     const DISABLED_FALSE = 3;
     const DISABLED_CHECKED = 4;
     const DISABLED_NOT_CHECKED = 5;
+    const DISABLED_EMPTY = 6;
+    const DISABLED_NOT_EMPTY = 7;
 
     /**
      * The parent setting (primary)
@@ -294,4 +296,108 @@ class setting_dependency_disabledif_not_checked extends setting_dependency_disab
             'condition'=>'notchecked'
         );
     }
+}
+
+/**
+ * A dependency that disables the secondary setting if the value of the primary setting
+ * is not empty.
+ *
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class setting_dependency_disabledif_not_empty extends setting_dependency_disabledif_equals {
+    public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
+        parent::__construct($setting, $dependentsetting, false, $defaultvalue);
+        $this->value = false;
+    }
+    /**
+     * Returns an array of properties suitable to be used to define a moodleforms
+     * disabled command
+     * @return array
+     */
+    public function get_moodleform_properties() {
+        return array(
+            'setting'=>$this->dependentsetting->get_ui_name(),
+            'dependenton'=>$this->setting->get_ui_name(),
+            'condition'=>'notequal',
+            'value'=>''
+        );
+    }
+    /**
+     * Processes a value change in the primary setting
+     * @param mixed $oldvalue
+     * @return bool
+     */
+    protected function process_value_change($oldvalue) {
+        $prevalue = $this->dependentsetting->get_value();
+        // If the setting is the desired value enact the dependency
+        $value = $this->setting->get_value();
+        if (!empty($value)) {
+            // The dependent setting needs to be locked by hierachy and set to the
+            // default value.
+            $this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
+            if ($this->defaultvalue === false) {
+                $this->dependentsetting->set_value($value);
+            } else {
+                $this->dependentsetting->set_value($this->defaultvalue);
+            }
+        } else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
+            // We can unlock the dependent setting
+            $this->dependentsetting->set_status(base_setting::NOT_LOCKED);
+        }
+        // Return true if the value has changed for the dependent setting
+        return ($prevalue != $this->dependentsetting->get_value());
+    }
+}
+
+/**
+ * A dependency that disables the secondary setting if the value of the primary setting
+ * is empty.
+ *
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class setting_dependency_disabledif_empty extends setting_dependency_disabledif_equals {
+    public function __construct(base_setting $setting, base_setting $dependentsetting, $defaultvalue = false) {
+        parent::__construct($setting, $dependentsetting, false, $defaultvalue);
+        $this->value = false;
+    }
+    /**
+     * Returns an array of properties suitable to be used to define a moodleforms
+     * disabled command
+     * @return array
+     */
+    public function get_moodleform_properties() {
+        return array(
+            'setting'=>$this->dependentsetting->get_ui_name(),
+            'dependenton'=>$this->setting->get_ui_name(),
+            'condition'=>'notequal',
+            'value'=>''
+        );
+    }
+    /**
+     * Processes a value change in the primary setting
+     * @param mixed $oldvalue
+     * @return bool
+     */
+    protected function process_value_change($oldvalue) {
+        $prevalue = $this->dependentsetting->get_value();
+        // If the setting is the desired value enact the dependency
+        $value = $this->setting->get_value();
+        if (empty($value)) {
+            // The dependent setting needs to be locked by hierachy and set to the
+            // default value.
+            $this->dependentsetting->set_status(base_setting::LOCKED_BY_HIERARCHY);
+            if ($this->defaultvalue === false) {
+                $this->dependentsetting->set_value($value);
+            } else {
+                $this->dependentsetting->set_value($this->defaultvalue);
+            }
+        } else if ($this->dependentsetting->get_status() == base_setting::LOCKED_BY_HIERARCHY) {
+            // We can unlock the dependent setting
+            $this->dependentsetting->set_status(base_setting::NOT_LOCKED);
+        }
+        // Return true if the value has changed for the dependent setting
+        return ($prevalue != $this->dependentsetting->get_value());
+    }
 }
\ No newline at end of file
index 4c56334..7bc9e65 100644 (file)
@@ -119,24 +119,33 @@ class setting_test extends UnitTestCase {
         }
 
         // Instantiate base_setting and try to set wrong ui_type
+        // We need a custom error handler to catch the type hinting error
+        // that should return incorrect_object_passed
         $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
+        set_error_handler('backup_setting_error_handler', E_RECOVERABLE_ERROR);
         try {
             $bs->set_ui('one_wrong_ui_type', 'label', array(), array());
             $this->assertTrue(false, 'base_setting_exception expected');
         } catch (exception $e) {
             $this->assertTrue($e instanceof base_setting_exception);
-            $this->assertEqual($e->errorcode, 'setting_invalid_ui_type');
+            $this->assertEqual($e->errorcode, 'incorrect_object_passed');
         }
+        restore_error_handler();
 
         // Instantiate base_setting and try to set wrong ui_label
+        // We need a custom error handler to catch the type hinting error
+        // that should return incorrect_object_passed
         $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN);
+        set_error_handler('backup_setting_error_handler', E_RECOVERABLE_ERROR);
         try {
             $bs->set_ui(base_setting::UI_HTML_CHECKBOX, 'one/wrong/label', array(), array());
             $this->assertTrue(false, 'base_setting_exception expected');
         } catch (exception $e) {
             $this->assertTrue($e instanceof base_setting_exception);
-            $this->assertEqual($e->errorcode, 'setting_invalid_ui_label');
+            $this->assertEqual($e->errorcode, 'incorrect_object_passed');
         }
+        restore_error_handler();
+        
         // Try to change value of locked setting by permission
         $bs = new mock_base_setting('test', base_setting::IS_BOOLEAN, null, null, base_setting::LOCKED_BY_PERMISSION);
         try {
@@ -160,7 +169,7 @@ class setting_test extends UnitTestCase {
         // Try to add same setting twice
         $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
         $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
-        $bs1->add_dependency($bs2);
+        $bs1->add_dependency($bs2, null, array('value'=>0));
         try {
             $bs1->add_dependency($bs2);
             $this->assertTrue(false, 'base_setting_exception expected');
@@ -186,11 +195,11 @@ class setting_test extends UnitTestCase {
         $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
         $bs3 = new mock_base_setting('test3', base_setting::IS_INTEGER, null);
         $bs4 = new mock_base_setting('test4', base_setting::IS_INTEGER, null);
-        $bs1->add_dependency($bs2);
-        $bs2->add_dependency($bs3);
-        $bs3->add_dependency($bs4);
+        $bs1->add_dependency($bs2, null, array('value'=>0));
+        $bs2->add_dependency($bs3, null, array('value'=>0));
+        $bs3->add_dependency($bs4, null, array('value'=>0));
         try {
-            $bs4->add_dependency($bs1);
+            $bs4->add_dependency($bs1, null, array('value'=>0));
             $this->assertTrue(false, 'base_setting_exception expected');
         } catch (exception $e) {
             $this->assertTrue($e instanceof base_setting_exception);
@@ -205,8 +214,8 @@ class setting_test extends UnitTestCase {
         $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
         $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
         $bs3 = new mock_base_setting('test3', base_setting::IS_INTEGER, null);
-        $bs1->add_dependency($bs2);
-        $bs2->add_dependency($bs3);
+        $bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
+        $bs2->add_dependency($bs3, setting_dependency::DISABLED_NOT_EMPTY);
         // Check values are spreaded ok
         $bs1->set_value(123);
         $this->assertEqual($bs1->get_value(), 123);
@@ -215,11 +224,13 @@ class setting_test extends UnitTestCase {
 
         // Add one more setting and set value again
         $bs4 = new mock_base_setting('test4', base_setting::IS_INTEGER, null);
-        $bs2->add_dependency($bs4);
+        $bs2->add_dependency($bs4, setting_dependency::DISABLED_NOT_EMPTY);
         $bs2->set_value(321);
+        // The above change should change
+        $this->assertEqual($bs1->get_value(), 123);
         $this->assertEqual($bs2->get_value(), 321);
-        $this->assertEqual($bs3->get_value(), $bs2->get_value());
-        $this->assertEqual($bs4->get_value(), $bs3->get_value());
+        $this->assertEqual($bs3->get_value(), 321);
+        $this->assertEqual($bs4->get_value(), 321);
 
         // Check visibility is spreaded ok
         $bs1->set_visibility(base_setting::HIDDEN);
@@ -236,8 +247,8 @@ class setting_test extends UnitTestCase {
         $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
         $bs2 = new mock_base_setting('test2', base_setting::IS_INTEGER, null);
         $bs3 = new mock_base_setting('test3', base_setting::IS_INTEGER, null);
-        $bs1->add_dependency($bs2);
-        $bs2->add_dependency($bs3);
+        $bs1->add_dependency($bs2, null, array('value'=>0));
+        $bs2->add_dependency($bs3, null, array('value'=>0));
         // Serialize
         $arr = array($bs1, $bs2, $bs3);
         $ser = base64_encode(serialize($arr));
@@ -259,19 +270,6 @@ class setting_test extends UnitTestCase {
         $this->assertEqual($ubs3->get_visibility(), $ubs1->get_visibility());
         $this->assertEqual($ubs2->get_status(), $ubs1->get_status());
         $this->assertEqual($ubs3->get_status(), $ubs1->get_status());
-
-        // Check ui_attributes
-        $bs1 = new mock_base_setting('test1', base_setting::IS_INTEGER, null);
-        $bs1->set_ui(base_setting::UI_HTML_DROPDOWN, 'dropdown', array(1 => 'One', 2 => 'Two'), array('opt1' => 1, 'opt2' => 2));
-        list($type, $label, $values, $options) = $bs1->get_ui_info();
-        $this->assertEqual($type, base_setting::UI_HTML_DROPDOWN);
-        $this->assertEqual($label, 'dropdown');
-        $this->assertEqual(count($values), 2);
-        $this->assertEqual($values[1], 'One');
-        $this->assertEqual($values[2], 'Two');
-        $this->assertEqual(count($options), 2);
-        $this->assertEqual($options['opt1'], 1);
-        $this->assertEqual($options['opt2'], 2);
     }
 
     /*
@@ -284,14 +282,16 @@ class setting_test extends UnitTestCase {
         $this->assertEqual($bs->get_level(), 1);
 
         // Instantiate backup setting class and try to add one non backup_setting dependency
+        set_error_handler('backup_setting_error_handler', E_RECOVERABLE_ERROR);
         $bs = new mock_backup_setting('test', base_setting::IS_INTEGER, null);
         try {
             $bs->add_dependency(new stdclass());
             $this->assertTrue(false, 'backup_setting_exception expected');
         } catch (exception $e) {
             $this->assertTrue($e instanceof backup_setting_exception);
-            $this->assertEqual($e->errorcode, 'dependency_is_not_backkup_setting');
+            $this->assertEqual($e->errorcode, 'incorrect_object_passed');
         }
+        restore_error_handler();
 
         // Try to assing upper level dependency
         $bs1 = new mock_backup_setting('test1', base_setting::IS_INTEGER, null);
@@ -311,7 +311,7 @@ class setting_test extends UnitTestCase {
         $bs1->set_level(1);
         $bs2 = new mock_backup_setting('test2', base_setting::IS_INTEGER, null);
         $bs2->set_level(1); // Same level *must* work
-        $bs1->add_dependency($bs2);
+        $bs1->add_dependency($bs2, setting_dependency::DISABLED_NOT_EMPTY);
         $bs1->set_value(123456);
         $this->assertEqual($bs2->get_value(), $bs1->get_value());
     }
@@ -425,3 +425,27 @@ class mock_course_backup_setting extends course_backup_setting {
         // Do nothing
     }
 }
+
+/**
+ * This error handler is used to convert errors to excpetions so that simepltest can
+ * catch them.
+ *
+ * This is required in order to catch type hint mismatches that result in a error
+ * being thrown. It should only ever be used to catch E_RECOVERABLE_ERROR's.
+ *
+ * It throws a backup_setting_exception with 'incorrect_object_passed'
+ *
+ * @param int $errno E_RECOVERABLE_ERROR
+ * @param string $errstr
+ * @param string $errfile
+ * @param int $errline
+ * @param array $errcontext
+ * @return null
+ */
+function backup_setting_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
+    if ($errno !== E_RECOVERABLE_ERROR) {
+        // Currently we only want to deal with type hinting errors
+        return false;
+    }
+    throw new backup_setting_exception('incorrect_object_passed');
+}
\ No newline at end of file
index dac4cce..4d841b6 100644 (file)
  * @copyright 2010 Sam Hemelryk
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-abstract class base_setting_ui {
+class base_setting_ui {
+    /**
+     * Prefix applied to all inputs/selects
+     */
+    const NAME_PREFIX = 'setting_';
+    /**
+     * The name of the setting
+     * @var string
+     */
+    protected $name;
+    /**
+     * The label for the setting
+     * @var string
+     */
+    protected $label;
+    /**
+     * An array of HTML attributes to apply to this setting
+     * @var array
+     */
+    protected $attributes = array();
+    /**
+     * The backup_setting UI type this relates to. One of backup_setting::UI_*;
+     * @var int
+     */
+    protected $type;
     /**
      * @var base_setting
      */
@@ -44,44 +68,69 @@ abstract class base_setting_ui {
         $this->setting = $setting;
     }
     /**
-     * Get element properties that can be used to make a quickform element
-     * @return array
+     * Gets the name of this item including its prefix
+     * @return string
      */
-    abstract public function get_element_properties(backup_task $task=null);
-}
-
-/**
- * Abstract class to represent the user interface backup settings have
- * 
- * @copyright 2010 Sam Hemelryk
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-abstract class backup_setting_ui extends base_setting_ui {
-
+    public function get_name() {
+        return self::NAME_PREFIX.$this->name;
+    }
     /**
-     * Prefix applied to all inputs/selects
+     * Gets the name of this item including its prefix
+     * @return string
      */
-    const NAME_PREFIX = 'setting_';
+    public function get_label() {
+        return $this->label;
+    }
     /**
-     * The backup_setting UI type this relates to. One of backup_setting::UI_*;
-     * @var int
+     * Gets the type of this element
+     * @return int
      */
-    protected $type;
+    public function get_type() {
+        return $this->type;
+    }
     /**
-     * The name of the setting
-     * @var string
+     * Gets the HTML attributes for this item
+     * @return array
      */
-    protected $name;
+    public function get_attributes() {
+        return $this->attributes;
+    }
     /**
-     * The label for the setting
-     * @var string
+     * Gets the value of this setting
+     * @return mixed
      */
-    protected $label;
+    public function get_value() {
+        return $this->setting->get_value();
+    }
     /**
-     * An array of HTML attributes to apply to this setting
-     * @var array
+     * Gets the value to display in a static quickforms element
+     * @return mixed
      */
-    protected $attributes = array();
+    public function get_static_value() {
+        return $this->setting->get_value();
+    }
+    /**
+     * Sets the label
+     * @param string $label
+     */
+    public function set_label($label) {
+        $this->label = $label;
+    }
+    /**
+     * Disables the UI for this element
+     */
+    public function disable() {
+       $this->attributes['disabled'] = 'disabled';
+    }
+}
+
+/**
+ * Abstract class to represent the user interface backup settings have
+ * 
+ * @copyright 2010 Sam Hemelryk
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class backup_setting_ui extends base_setting_ui {
     /**
      * An array of options relating to this setting
      * @var array
@@ -99,6 +148,7 @@ abstract class backup_setting_ui extends base_setting_ui {
     public function __construct(backup_setting $setting, $label = null, array $attributes = null, array $options = null) {
         parent::__construct($setting);
         // Improve the inputs name by appending the level to the name
+        if (!($setting instanceof backup_setting)) debug($setting);
         switch ($setting->get_level()) {
             case backup_setting::ROOT_LEVEL :
                 $this->name = 'root_'.$setting->get_name();
@@ -146,6 +196,11 @@ abstract class backup_setting_ui extends base_setting_ui {
                 return false;
         }
     }
+    /**
+     * Get element properties that can be used to make a quickform element
+     * @return array
+     */
+    abstract public function get_element_properties(backup_task $task=null);
     /**
      * Applies config options to a given properties array and then returns it
      * @param array $properties
@@ -157,20 +212,6 @@ abstract class backup_setting_ui extends base_setting_ui {
         }
         return $properties;
     }
-    /**
-     * Gets the name of this item including its prefix
-     * @return string
-     */
-    public function get_name() {
-        return self::NAME_PREFIX.$this->name;
-    }
-    /**
-     * Gets the type of this element
-     * @return int
-     */
-    public function get_type() {
-        return $this->type;
-    }
     /**
      * Gets the label for this item
      * @param backup_task|null $task Optional, if provided and the setting is an include
@@ -189,41 +230,6 @@ abstract class backup_setting_ui extends base_setting_ui {
         }
         return $this->label;
     }
-    /**
-     * Gets the HTML attributes for this item
-     * @return array
-     */
-    public function get_attributes() {
-        return $this->attributes;
-    }
-    /**
-     * Gets the value of this setting
-     * @return mixed
-     */
-    public function get_value() {
-        return $this->setting->get_value();
-    }
-    /**
-     * Gets the value to display in a static quickforms element
-     * @return mixed
-     */
-    public function get_static_value() {
-        return $this->setting->get_value();
-    }
-    /**
-     * Sets the label
-     * @param string $label
-     */
-    public function set_label($label) {
-        $this->label = $label;
-    }
-    /**
-     * Disables the UI for this element
-     */
-    public function disable() {
-       $this->attributes['disabled'] = 'disabled';
-    }
-
 }
 
 /**