See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 core; 18 19 use admin_category; 20 use admin_externalpage; 21 use admin_root; 22 use admin_settingpage; 23 use admin_setting_configdirectory; 24 use admin_setting_configduration; 25 use admin_setting_configexecutable; 26 use admin_setting_configfile; 27 use admin_setting_configmixedhostiplist; 28 use admin_setting_configpasswordunmask; 29 use admin_setting_configtext; 30 31 defined('MOODLE_INTERNAL') || die(); 32 33 global $CFG; 34 require_once($CFG->libdir.'/adminlib.php'); 35 36 /** 37 * Provides the unit tests for admin tree functionality. 38 * 39 * @package core 40 * @category test 41 * @copyright 2013 David Mudrak <david@moodle.com> 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 */ 44 class admintree_test extends \advanced_testcase { 45 46 /** 47 * Adding nodes into the admin tree. 48 */ 49 public function test_add_nodes() { 50 51 $tree = new admin_root(true); 52 $tree->add('root', $one = new admin_category('one', 'One')); 53 $tree->add('root', new admin_category('three', 'Three')); 54 $tree->add('one', new admin_category('one-one', 'One-one')); 55 $tree->add('one', new admin_category('one-three', 'One-three')); 56 57 // Check the order of nodes in the root. 58 $map = array(); 59 foreach ($tree->children as $child) { 60 $map[] = $child->name; 61 } 62 $this->assertEquals(array('one', 'three'), $map); 63 64 // Insert a node into the middle. 65 $tree->add('root', new admin_category('two', 'Two'), 'three'); 66 $map = array(); 67 foreach ($tree->children as $child) { 68 $map[] = $child->name; 69 } 70 $this->assertEquals(array('one', 'two', 'three'), $map); 71 72 // Non-existing sibling. 73 $tree->add('root', new admin_category('four', 'Four'), 'five'); 74 $this->assertDebuggingCalled('Sibling five not found', DEBUG_DEVELOPER); 75 76 $tree->add('root', new admin_category('five', 'Five')); 77 $map = array(); 78 foreach ($tree->children as $child) { 79 $map[] = $child->name; 80 } 81 $this->assertEquals(array('one', 'two', 'three', 'four', 'five'), $map); 82 83 // Insert a node into the middle of the subcategory. 84 $tree->add('one', new admin_category('one-two', 'One-two'), 'one-three'); 85 $map = array(); 86 foreach ($one->children as $child) { 87 $map[] = $child->name; 88 } 89 $this->assertEquals(array('one-one', 'one-two', 'one-three'), $map); 90 91 // Check just siblings, not parents or children. 92 $tree->add('one', new admin_category('one-four', 'One-four'), 'one'); 93 $this->assertDebuggingCalled('Sibling one not found', DEBUG_DEVELOPER); 94 95 $tree->add('root', new admin_category('six', 'Six'), 'one-two'); 96 $this->assertDebuggingCalled('Sibling one-two not found', DEBUG_DEVELOPER); 97 98 // Me! Me! I wanna be first! 99 $tree->add('root', new admin_externalpage('zero', 'Zero', 'http://foo.bar'), 'one'); 100 $map = array(); 101 foreach ($tree->children as $child) { 102 $map[] = $child->name; 103 } 104 $this->assertEquals(array('zero', 'one', 'two', 'three', 'four', 'five', 'six'), $map); 105 } 106 107 public function test_add_nodes_before_invalid1() { 108 $tree = new admin_root(true); 109 $this->expectException(\coding_exception::class); 110 $tree->add('root', new admin_externalpage('foo', 'Foo', 'http://foo.bar'), array('moodle:site/config')); 111 } 112 113 public function test_add_nodes_before_invalid2() { 114 $tree = new admin_root(true); 115 $this->expectException(\coding_exception::class); 116 $tree->add('root', new admin_category('bar', 'Bar'), ''); 117 } 118 119 /** 120 * Test that changes to config trigger events. 121 */ 122 public function test_config_log_created_event() { 123 global $DB; 124 $this->resetAfterTest(); 125 $this->setAdminUser(); 126 127 $adminroot = new admin_root(true); 128 $adminroot->add('root', $one = new admin_category('one', 'One')); 129 $page = new admin_settingpage('page', 'Page'); 130 $page->add(new admin_setting_configtext('text1', 'Text 1', '', '')); 131 $page->add(new admin_setting_configpasswordunmask('pass1', 'Password 1', '', '')); 132 $adminroot->add('one', $page); 133 134 $sink = $this->redirectEvents(); 135 $data = array('s__text1' => 'sometext', 's__pass1' => ''); 136 $this->save_config_data($adminroot, $data); 137 138 $events = $sink->get_events(); 139 $sink->close(); 140 $event = array_pop($events); 141 $this->assertInstanceOf('\core\event\config_log_created', $event); 142 143 $sink = $this->redirectEvents(); 144 $data = array('s__text1'=>'other', 's__pass1'=>'nice password'); 145 $count = $this->save_config_data($adminroot, $data); 146 147 $events = $sink->get_events(); 148 $sink->close(); 149 $event = array_pop($events); 150 $this->assertInstanceOf('\core\event\config_log_created', $event); 151 // Verify password was nuked. 152 $this->assertNotEquals($event->other['value'], 'nice password'); 153 154 } 155 156 /** 157 * Testing whether a configexecutable setting is executable. 158 */ 159 public function test_admin_setting_configexecutable() { 160 global $CFG; 161 $this->resetAfterTest(); 162 163 $CFG->theme = 'classic'; 164 $executable = new admin_setting_configexecutable('test1', 'Text 1', 'Help Path', ''); 165 166 // Check for an invalid path. 167 $result = $executable->output_html($CFG->dirroot . '/lib/tests/other/file_does_not_exist'); 168 $this->assertMatchesRegularExpression('/class="text-danger"/', $result); 169 170 // Check for a directory. 171 $result = $executable->output_html($CFG->dirroot); 172 $this->assertMatchesRegularExpression('/class="text-danger"/', $result); 173 174 // Check for a file which is not executable. 175 $result = $executable->output_html($CFG->dirroot . '/filter/tex/readme_moodle.txt'); 176 $this->assertMatchesRegularExpression('/class="text-danger"/', $result); 177 178 // Check for an executable file. 179 if ($CFG->ostype == 'WINDOWS') { 180 $filetocheck = 'mimetex.exe'; 181 } else { 182 $filetocheck = 'mimetex.darwin'; 183 } 184 $result = $executable->output_html($CFG->dirroot . '/filter/tex/' . $filetocheck); 185 $this->assertMatchesRegularExpression('/class="text-success"/', $result); 186 187 // Check for no file specified. 188 $result = $executable->output_html(''); 189 $this->assertMatchesRegularExpression('/name="s__test1"/', $result); 190 $this->assertMatchesRegularExpression('/value=""/', $result); 191 } 192 193 /** 194 * Saving of values. 195 */ 196 public function test_config_logging() { 197 global $DB; 198 $this->resetAfterTest(); 199 $this->setAdminUser(); 200 201 $DB->delete_records('config_log', array()); 202 203 $adminroot = new admin_root(true); 204 $adminroot->add('root', $one = new admin_category('one', 'One')); 205 $page = new admin_settingpage('page', 'Page'); 206 $page->add(new admin_setting_configtext('text1', 'Text 1', '', '')); 207 $page->add(new admin_setting_configpasswordunmask('pass1', 'Password 1', '', '')); 208 $adminroot->add('one', $page); 209 210 $this->assertEmpty($DB->get_records('config_log')); 211 $data = array('s__text1'=>'sometext', 's__pass1'=>''); 212 $count = $this->save_config_data($adminroot, $data); 213 214 $this->assertEquals(2, $count); 215 $records = $DB->get_records('config_log', array(), 'id asc'); 216 $this->assertCount(2, $records); 217 reset($records); 218 $record = array_shift($records); 219 $this->assertNull($record->plugin); 220 $this->assertSame('text1', $record->name); 221 $this->assertNull($record->oldvalue); 222 $this->assertSame('sometext', $record->value); 223 $record = array_shift($records); 224 $this->assertNull($record->plugin); 225 $this->assertSame('pass1', $record->name); 226 $this->assertNull($record->oldvalue); 227 $this->assertSame('', $record->value); 228 229 $DB->delete_records('config_log', array()); 230 $data = array('s__text1'=>'other', 's__pass1'=>'nice password'); 231 $count = $this->save_config_data($adminroot, $data); 232 233 $this->assertEquals(2, $count); 234 $records = $DB->get_records('config_log', array(), 'id asc'); 235 $this->assertCount(2, $records); 236 reset($records); 237 $record = array_shift($records); 238 $this->assertNull($record->plugin); 239 $this->assertSame('text1', $record->name); 240 $this->assertSame('sometext', $record->oldvalue); 241 $this->assertSame('other', $record->value); 242 $record = array_shift($records); 243 $this->assertNull($record->plugin); 244 $this->assertSame('pass1', $record->name); 245 $this->assertSame('', $record->oldvalue); 246 $this->assertSame('********', $record->value); 247 248 $DB->delete_records('config_log', array()); 249 $data = array('s__text1'=>'', 's__pass1'=>''); 250 $count = $this->save_config_data($adminroot, $data); 251 252 $this->assertEquals(2, $count); 253 $records = $DB->get_records('config_log', array(), 'id asc'); 254 $this->assertCount(2, $records); 255 reset($records); 256 $record = array_shift($records); 257 $this->assertNull($record->plugin); 258 $this->assertSame('text1', $record->name); 259 $this->assertSame('other', $record->oldvalue); 260 $this->assertSame('', $record->value); 261 $record = array_shift($records); 262 $this->assertNull($record->plugin); 263 $this->assertSame('pass1', $record->name); 264 $this->assertSame('********', $record->oldvalue); 265 $this->assertSame('', $record->value); 266 } 267 268 protected function save_config_data(admin_root $adminroot, array $data) { 269 $adminroot->errors = array(); 270 271 $settings = admin_find_write_settings($adminroot, $data); 272 273 $count = 0; 274 foreach ($settings as $fullname=>$setting) { 275 /** @var $setting admin_setting */ 276 $original = $setting->get_setting(); 277 $error = $setting->write_setting($data[$fullname]); 278 if ($error !== '') { 279 $adminroot->errors[$fullname] = new \stdClass(); 280 $adminroot->errors[$fullname]->data = $data[$fullname]; 281 $adminroot->errors[$fullname]->id = $setting->get_id(); 282 $adminroot->errors[$fullname]->error = $error; 283 } else { 284 $setting->write_setting_flags($data); 285 } 286 if ($setting->post_write_settings($original)) { 287 $count++; 288 } 289 } 290 291 return $count; 292 } 293 294 public function test_preventexecpath() { 295 $this->resetAfterTest(); 296 297 set_config('preventexecpath', 0); 298 set_config('execpath', null, 'abc_cde'); 299 $this->assertFalse(get_config('abc_cde', 'execpath')); 300 $setting = new admin_setting_configexecutable('abc_cde/execpath', 'some desc', '', '/xx/yy'); 301 $setting->write_setting('/oo/pp'); 302 $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); 303 304 // Prevent changes. 305 set_config('preventexecpath', 1); 306 $setting->write_setting('/mm/nn'); 307 $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); 308 309 // Use default in install. 310 set_config('execpath', null, 'abc_cde'); 311 $setting->write_setting('/mm/nn'); 312 $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath')); 313 314 // Use empty value if no default. 315 $setting = new admin_setting_configexecutable('abc_cde/execpath', 'some desc', '', null); 316 set_config('execpath', null, 'abc_cde'); 317 $setting->write_setting('/mm/nn'); 318 $this->assertSame('', get_config('abc_cde', 'execpath')); 319 320 // This also affects admin_setting_configfile and admin_setting_configdirectory. 321 322 set_config('preventexecpath', 0); 323 set_config('execpath', null, 'abc_cde'); 324 $this->assertFalse(get_config('abc_cde', 'execpath')); 325 $setting = new admin_setting_configfile('abc_cde/execpath', 'some desc', '', '/xx/yy'); 326 $setting->write_setting('/oo/pp'); 327 $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); 328 329 // Prevent changes. 330 set_config('preventexecpath', 1); 331 $setting->write_setting('/mm/nn'); 332 $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); 333 334 // Use default in install. 335 set_config('execpath', null, 'abc_cde'); 336 $setting->write_setting('/mm/nn'); 337 $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath')); 338 339 // Use empty value if no default. 340 $setting = new admin_setting_configfile('abc_cde/execpath', 'some desc', '', null); 341 set_config('execpath', null, 'abc_cde'); 342 $setting->write_setting('/mm/nn'); 343 $this->assertSame('', get_config('abc_cde', 'execpath')); 344 345 set_config('preventexecpath', 0); 346 set_config('execpath', null, 'abc_cde'); 347 $this->assertFalse(get_config('abc_cde', 'execpath')); 348 $setting = new admin_setting_configdirectory('abc_cde/execpath', 'some desc', '', '/xx/yy'); 349 $setting->write_setting('/oo/pp'); 350 $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); 351 352 // Prevent changes. 353 set_config('preventexecpath', 1); 354 $setting->write_setting('/mm/nn'); 355 $this->assertSame('/oo/pp', get_config('abc_cde', 'execpath')); 356 357 // Use default in install. 358 set_config('execpath', null, 'abc_cde'); 359 $setting->write_setting('/mm/nn'); 360 $this->assertSame('/xx/yy', get_config('abc_cde', 'execpath')); 361 362 // Use empty value if no default. 363 $setting = new admin_setting_configdirectory('abc_cde/execpath', 'some desc', '', null); 364 set_config('execpath', null, 'abc_cde'); 365 $setting->write_setting('/mm/nn'); 366 $this->assertSame('', get_config('abc_cde', 'execpath')); 367 } 368 369 /** 370 * Test setting an empty duration displays the correct validation message. 371 */ 372 public function test_emptydurationvalue() { 373 $this->resetAfterTest(); 374 $adminsetting = new admin_setting_configduration('abc_cde/duration', 'some desc', '', ''); 375 376 // A value that isn't a number is treated as a zero, so we expect to see no error message. 377 $this->assertEmpty($adminsetting->write_setting(['u' => '3600', 'v' => 'abc'])); 378 } 379 380 /** 381 * Test setting for blocked hosts 382 * 383 * For testing the admin settings element only. Test for blocked hosts functionality can be found 384 * in lib/tests/curl_security_helper_test.php 385 */ 386 public function test_mixedhostiplist() { 387 $this->resetAfterTest(); 388 389 $adminsetting = new admin_setting_configmixedhostiplist('abc_cde/hostiplist', 'some desc', '', ''); 390 391 // Test valid settings. 392 $validsimplesettings = [ 393 'localhost', 394 "localhost\n127.0.0.1", 395 '192.168.10.1', 396 '0:0:0:0:0:0:0:1', 397 '::1', 398 'fe80::', 399 '231.54.211.0/20', 400 'fe80::/64', 401 '231.3.56.10-20', 402 'fe80::1111-bbbb', 403 '*.example.com', 404 '*.sub.example.com', 405 ]; 406 407 foreach ($validsimplesettings as $setting) { 408 $errormessage = $adminsetting->write_setting($setting); 409 $this->assertEmpty($errormessage, $errormessage); 410 $this->assertSame($setting, get_config('abc_cde', 'hostiplist')); 411 $this->assertSame($setting, $adminsetting->get_setting()); 412 } 413 414 // Test valid international site names. 415 $valididnsettings = [ 416 'правительство.рф' => 'xn--80aealotwbjpid2k.xn--p1ai', 417 'faß.de' => 'xn--fa-hia.de', 418 'ß.ß' => 'xn--zca.xn--zca', 419 '*.tharkûn.com' => '*.xn--tharkn-0ya.com', 420 ]; 421 422 foreach ($valididnsettings as $setting => $encodedsetting) { 423 $errormessage = $adminsetting->write_setting($setting); 424 $this->assertEmpty($errormessage, $errormessage); 425 $this->assertSame($encodedsetting, get_config('abc_cde', 'hostiplist')); 426 $this->assertSame($setting, $adminsetting->get_setting()); 427 } 428 429 // Invalid settings. 430 $this->assertEquals('These entries are invalid: nonvalid site name', $adminsetting->write_setting('nonvalid site name')); 431 $this->assertEquals('Empty lines are not valid', $adminsetting->write_setting("localhost\n")); 432 } 433 434 /** 435 * Verifies the $ADMIN global (adminroot cache) is properly reset when changing users, which might occur naturally during cron. 436 */ 437 public function test_adminroot_cache_reset() { 438 $this->resetAfterTest(); 439 global $DB; 440 // Current user is a manager at site context, which won't have access to the 'debugging' section of the admin tree. 441 $manageruser = $this->getDataGenerator()->create_user(); 442 $context = \context_system::instance(); 443 $managerrole = $DB->get_record('role', array('shortname' => 'manager')); 444 role_assign($managerrole->id, $manageruser->id, $context->id); 445 $this->setUser($manageruser); 446 $adminroot = admin_get_root(); 447 $section = $adminroot->locate('debugging'); 448 $this->assertEmpty($section); 449 450 // Now, change the user to an admin user and confirm we get a new copy of the admin tree when next we ask for it. 451 $adminuser = get_admin(); 452 $this->setUser($adminuser); 453 $adminroot = admin_get_root(); 454 $section = $adminroot->locate('debugging'); 455 $this->assertInstanceOf('\admin_settingpage', $section); 456 } 457 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body