MDL-21432 backup 2.0 - initial commit. util dir
[moodle.git] / backup / util / settings / base_setting.class.php
CommitLineData
69dd0c8c
EL
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * @package moodlecore
20 * @subpackage backup-settings
21 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25/**
26 * This abstract class defines one basic setting
27 *
28 * Each setting will be able to control its name, value (from a list), ui
29 * representation (check box, drop down, text field...), visibility, status
30 * (editable/locked...) and its hierarchy with other settings (using one
31 * like-observer pattern.
32 *
33 * TODO: Finish phpdocs
34 */
35abstract class base_setting {
36
37 // Some constants defining different ui representations for the setting
38 const UI_NONE = 0;
39 const UI_HTML_CHECKBOX = 10;
40 const UI_HTML_RADIOBUTTON = 20;
41 const UI_HTML_DROPDOWN = 30;
42 const UI_HTML_TEXTFIELD = 40;
43
44 // Type of validation to perform against the value (relaying in PARAM_XXX validations)
45 const IS_BOOLEAN = 'bool';
46 const IS_INTEGER = 'int';
47 const IS_FILENAME= 'file';
48 const IS_PATH = 'path';
49
50 // Visible/hidden
51 const VISIBLE = 1;
52 const HIDDEN = 0;
53
54 // Editable/locked (by different causes)
55 const NOT_LOCKED = 5;
56 const LOCKED_BY_PERMISSION = 6;
57 const LOCKED_BY_HIERARCHY = 7;
58
59 // Type of change to inform dependencies
60 const CHANGED_VALUE = 1;
61 const CHANGED_VISIBILITY = 2;
62 const CHANGED_STATUS = 3;
63
64 protected $name; // name of the setting
65 protected $value; // value of the setting
66 protected $vtype; // type of value (setting_base::IS_BOOLEAN/setting_base::IS_INTEGER...)
67
68 protected $visibility; // visibility of the setting (setting_base::VISIBLE/setting_base::HIDDEN)
69 protected $status; // setting_base::NOT_LOCKED/setting_base::LOCKED_BY_PERMISSION...
70
71 protected $dependencies; // array of dependent (observer) objects (usually setting_base ones)
72
73 // Note: all the UI stuff could go to independent classes in the future...
74 protected $ui_type; // setting_base::UI_HTML_CHECKBOX/setting_base::UI_HTML_RADIOBUTTON...
75 protected $ui_label; // UI label of the setting
76 protected $ui_values; // array of value => ui value of the setting
77 protected $ui_options;// array of custom ui options
78
79 public function __construct($name, $vtype, $value = null, $visibility = self::VISIBLE, $status = self::NOT_LOCKED) {
80 // Check vtype
81 if ($vtype !== self::IS_BOOLEAN && $vtype !== self::IS_INTEGER &&
82 $vtype !== self::IS_FILENAME && $vtype !== self::IS_PATH) {
83 throw new base_setting_exception('setting_invalid_type');
84 }
85
86 // Validate value
87 $value = $this->validate_value($vtype, $value);
88
89 // Check visibility
90 $visibility = $this->validate_visibility($visibility);
91
92 // Check status
93 $status = $this->validate_status($status);
94
95 $this->name = $name;
96 $this->vtype = $vtype;
97 $this->value = $value;
98 $this->visibility = $visibility;
99 $this->status = $status;
100 $this->dependencies= array();
101
102 // Apply these defaults
103 $this->ui_type = self::UI_HTML_DROPDOWN;
104 $this->ui_label = $name;
105 $this->ui_values = array();
106 $this->ui_options = array();
107 }
108
109 public function get_name() {
110 return $this->name;
111 }
112
113 public function get_value() {
114 return $this->value;
115 }
116
117 public function get_visibility() {
118 return $this->visibility;
119 }
120
121 public function get_status() {
122 return $this->status;
123 }
124
125 public function set_value($value) {
126 // Validate value
127 $value = $this->validate_value($this->vtype, $value);
128 // Only can change value if setting is not locked
129 if ($this->status != self::NOT_LOCKED) {
130 switch ($this->status) {
131 case self::LOCKED_BY_PERMISSION:
132 throw new base_setting_exception('setting_locked_by_permission');
133 case self::LOCKED_BY_HIERARCHY:
134 throw new base_setting_exception('setting_locked_by_hierarchy');
135 }
136 }
137 $oldvalue = $this->value;
138 $this->value = $value;
139 if ($value !== $oldvalue) { // Value has changed, let's inform dependencies
140 $this->inform_dependencies(self::CHANGED_VALUE, $oldvalue);
141 }
142 }
143
144 public function set_visibility($visibility) {
145 $visibility = $this->validate_visibility($visibility);
146 $oldvisibility = $this->visibility;
147 $this->visibility = $visibility;
148 if ($visibility !== $oldvisibility) { // Visibility has changed, let's inform dependencies
149 $this->inform_dependencies(self::CHANGED_VISIBILITY, $oldvisibility);
150 }
151 }
152
153 public function set_status($status) {
154 $status = $this->validate_status($status);
155 $oldstatus = $this->status;
156 $this->status = $status;
157 if ($status !== $oldstatus) { // Status has changed, let's inform dependencies
158 $this->inform_dependencies(self::CHANGED_STATUS, $oldstatus);
159 }
160 }
161
162 public function set_ui($type, $label, $values, $options) {
163 $type = $this->validate_ui_type($type);
164 $label =$this->validate_ui_label($label);
165 $this->ui_type = $type;
166 $this->ui_label = $label;
167 $this->set_ui_values($values);
168 $this->set_ui_options($options);
169 }
170
171 public function set_ui_values($values) {
172 $this->ui_values = $values;
173 }
174
175 public function set_ui_options($options) {
176 $this->ui_options = $options;
177 }
178
179 public function add_dependency($obj) {
180 if ($this->is_circular_reference($obj)) {
181 $a = new stdclass();
182 $a->alreadydependent = $this->name;
183 $a->main = $obj->get_name();
184 throw new base_setting_exception('setting_circular_reference', $a);
185 }
186 // Check the settings hasn't been already added
187 if (array_key_exists($obj->get_name(), $this->dependencies)) {
188 throw new base_setting_exception('setting_already_added');
189 }
190 $this->dependencies[$obj->get_name()] = $obj;
191 }
192
193// Protected API starts here
194
195 protected function validate_value($vtype, $value) {
196 if (is_null($value)) { // Nulls aren't validated
197 return null;
198 }
199 $oldvalue = $value;
200 switch ($vtype) {
201 case self::IS_BOOLEAN:
202 $value = clean_param($oldvalue, PARAM_BOOL); // Just clean
203 break;
204 case self::IS_INTEGER:
205 $value = clean_param($oldvalue, PARAM_INT);
206 if ($value != $oldvalue) {
207 throw new base_setting_exception('setting_invalid_integer', $oldvalue);
208 }
209 break;
210 case self::IS_FILENAME:
211 $value = clean_param($oldvalue, PARAM_FILE);
212 if ($value != $oldvalue) {
213 throw new base_setting_exception('setting_invalid_filename', $oldvalue);
214 }
215 break;
216 case self::IS_PATH:
217 $value = clean_param($oldvalue, PARAM_PATH);
218 if ($value != $oldvalue) {
219 throw new base_setting_exception('setting_invalid_path', $oldvalue);
220 }
221 break;
222 }
223 return $value;
224 }
225
226 protected function validate_visibility($visibility) {
227 if (is_null($visibility)) {
228 $visibility = self::VISIBLE;
229 }
230 if ($visibility !== self::VISIBLE && $visibility !== self::HIDDEN) {
231 throw new base_setting_exception('setting_invalid_visibility');
232 }
233 return $visibility;
234 }
235
236 protected function validate_status($status) {
237 if (is_null($status)) {
238 $status = self::NOT_LOCKED;
239 }
240 if ($status !== self::NOT_LOCKED && $status !== self::LOCKED_BY_PERMISSION && $status !== self::LOCKED_BY_HIERARCHY) {
241 throw new base_setting_exception('setting_invalid_status');
242 }
243 return $status;
244 }
245
246 protected function validate_ui_type($type) {
247 if ($type !== self::UI_HTML_CHECKBOX && $type !== self::UI_HTML_RADIOBUTTON &&
248 $type !== self::UI_HTML_DROPDOWN && $type !== self::UI_HTML_TEXTFIELD) {
249 throw new base_setting_exception('setting_invalid_ui_type');
250 }
251 return $type;
252 }
253
254 protected function validate_ui_label($label) {
255 if (empty($label) || $label !== clean_param($label, PARAM_ALPHAEXT)) {
256 throw new base_setting_exception('setting_invalid_ui_label');
257 }
258 return $label;
259 }
260
261 protected function inform_dependencies($ctype, $oldv) {
262 foreach ($this->dependencies as $dependency) {
263 $dependency->process_change($this, $ctype, $oldv);
264 }
265 }
266
267 protected function is_circular_reference($obj) {
268 // Get object dependencies recursively and check (by name) if $this is already there
269 $dependencies = $obj->get_dependencies();
270 if (array_key_exists($this->name, $dependencies) || $obj == $this) {
271 return true;
272 }
273 return false;
274 }
275
276 protected function get_dependencies() {
277 $dependencies = array();
278 foreach ($this->dependencies as $dependency) {
279 $dependencies[$dependency->get_name()] = $dependency->get_name();
280 $dependencies = array_merge($dependencies, $dependency->get_dependencies());
281 }
282 return $dependencies;
283 }
284
285// Implementable API starts here
286
287 abstract public function process_change($setting, $ctype, $oldv);
288}
289
290/*
291 * Exception class used by all the @setting_base stuff
292 */
293class base_setting_exception extends backup_exception {
294
295 public function __construct($errorcode, $a=NULL, $debuginfo=null) {
296 parent::__construct($errorcode, $a, $debuginfo);
297 }
298}