diff --git a/admin/settings/location.php b/admin/settings/location.php index 504cbb9aa06..5a4cc9010c8 100644 --- a/admin/settings/location.php +++ b/admin/settings/location.php @@ -1,21 +1,57 @@ . -if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page +/** + * Define administration settings on the Location settings page. + * + * @package core + * @category admin + * @copyright 2006 Martin Dougiamas + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ - // "locations" settingpage - $temp = new admin_settingpage('locationsettings', new lang_string('locationsettings', 'admin')); - $temp->add(new admin_setting_servertimezone()); - $temp->add(new admin_setting_forcetimezone()); - $temp->add(new admin_settings_country_select('country', new lang_string('country', 'admin'), new lang_string('configcountry', 'admin'), 0)); - $temp->add(new admin_setting_configtext('defaultcity', new lang_string('defaultcity', 'admin'), new lang_string('defaultcity_help', 'admin'), '')); +defined('MOODLE_INTERNAL') || die(); - $temp->add(new admin_setting_heading('iplookup', new lang_string('iplookup', 'admin'), new lang_string('iplookupinfo', 'admin'))); - $temp->add(new admin_setting_configfile('geoip2file', new lang_string('geoipfile', 'admin'), - new lang_string('configgeoipfile', 'admin', $CFG->dataroot.'/geoip/'), $CFG->dataroot.'/geoip/GeoLite2-City.mmdb')); - $temp->add(new admin_setting_configtext('googlemapkey3', new lang_string('googlemapkey3', 'admin'), new lang_string('googlemapkey3_help', 'admin'), '', PARAM_RAW, 60)); +if ($hassiteconfig) { + $temp = new admin_settingpage('locationsettings', new lang_string('locationsettings', 'core_admin')); - $temp->add(new admin_setting_configtext('allcountrycodes', new lang_string('allcountrycodes', 'admin'), new lang_string('configallcountrycodes', 'admin'), '', '/^(?:\w+(?:,\w+)*)?$/')); + if ($ADMIN->fulltree) { + $temp->add(new admin_setting_servertimezone()); + + $temp->add(new admin_setting_forcetimezone()); + + $temp->add(new admin_settings_country_select('country', new lang_string('country', 'core_admin'), + new lang_string('configcountry', 'core_admin'), 0)); + + $temp->add(new admin_setting_configtext('defaultcity', new lang_string('defaultcity', 'core_admin'), + new lang_string('defaultcity_help', 'core_admin'), '')); + + $temp->add(new admin_setting_heading('iplookup', new lang_string('iplookup', 'core_admin'), + new lang_string('iplookupinfo', 'core_admin'))); + + $temp->add(new admin_setting_configfile('geoip2file', new lang_string('geoipfile', 'core_admin'), + new lang_string('configgeoipfile', 'core_admin', $CFG->dataroot . '/geoip/'), + $CFG->dataroot . '/geoip/GeoLite2-City.mmdb')); + + $temp->add(new admin_setting_configtext('googlemapkey3', new lang_string('googlemapkey3', 'core_admin'), + new lang_string('googlemapkey3_help', 'core_admin'), '', PARAM_RAW, 60)); + + $temp->add(new admin_setting_countrycodes('allcountrycodes', new lang_string('allcountrycodes', 'core_admin'), + new lang_string('configallcountrycodes', 'core_admin'))); + } $ADMIN->add('location', $temp); - -} // end of speedup +} diff --git a/admin/tests/behat/invalid_allcountrycodes.feature b/admin/tests/behat/invalid_allcountrycodes.feature new file mode 100644 index 00000000000..399712f45e1 --- /dev/null +++ b/admin/tests/behat/invalid_allcountrycodes.feature @@ -0,0 +1,29 @@ +@core @core_admin +Feature: Administrator is warned and when trying to set invalid allcountrycodes value. + In order to avoid misconfiguration of the country selector fields + As an admin + I want to be warned when I try to set an invalid country code in the allcountrycodes field + + Scenario: Attempting to set allcountrycodes field with valid country codes + Given I log in as "admin" + And I navigate to "Location > Location settings" in site administration + When I set the following administration settings values: + | All country codes | CZ,BE,GB,ES | + Then I should not see "Invalid country code" + + Scenario: Attempting to set allcountrycodes field with invalid country code + Given I log in as "admin" + And I navigate to "Location > Location settings" in site administration + When I set the following administration settings values: + | All country codes | CZ,BE,FOOBAR,GB,ES | + Then I should see "Invalid country code: FOOBAR" + + Scenario: Attempting to unset allcountrycodes field + Given I log in as "admin" + And I navigate to "Location > Location settings" in site administration + And I set the following administration settings values: + | All country codes | CZ,BE,GB,ES | + And I navigate to "Location > Location settings" in site administration + When I set the following administration settings values: + | All country codes | | + Then I should not see "Invalid country code" diff --git a/lang/en/error.php b/lang/en/error.php index 828a2f52cc5..7530dedd2f9 100644 --- a/lang/en/error.php +++ b/lang/en/error.php @@ -324,6 +324,7 @@ $string['invalidcourselevel'] = 'Incorrect context level'; $string['invalidcourseformat'] = 'Invalid course format'; $string['invalidcoursemodule'] = 'Invalid course module ID'; $string['invalidcoursenameshort'] = 'Invalid short course name'; +$string['invalidcountrycode'] = 'Invalid country code: {$a}'; $string['invaliddata'] = 'Data submitted is invalid'; $string['invaliddatarootpermissions'] = 'Invalid permissions detected when trying to create a directory. Turn debugging on for further details.'; $string['invaliddevicetype'] = 'Invalid device type'; diff --git a/lib/adminlib.php b/lib/adminlib.php index e8206687316..eb7476a0ce5 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -4947,6 +4947,66 @@ class admin_setting_langlist extends admin_setting_configtext { } +/** + * Allows to specify comma separated list of known country codes. + * + * This is a simple subclass of the plain input text field with added validation so that all the codes are actually + * known codes. + * + * @package core + * @category admin + * @copyright 2020 David Mudrák + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_setting_countrycodes extends admin_setting_configtext { + + /** + * Construct the instance of the setting. + * + * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'. + * @param lang_string|string $visiblename Language string with the field label text. + * @param lang_string|string $description Language string with the field description text. + * @param string $defaultsetting Default value of the setting. + * @param int $size Input text field size. + */ + public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) { + parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size); + } + + /** + * Validate the setting value before storing it. + * + * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or + * a comma separated list of such words. + * + * @param string $data Value inserted into the setting field. + * @return bool|string True if the value is OK, error string otherwise. + */ + public function validate($data) { + + $parentcheck = parent::validate($data); + + if ($parentcheck !== true) { + return $parentcheck; + } + + if ($data === '') { + return true; + } + + $allcountries = get_string_manager()->get_list_of_countries(true); + + foreach (explode(',', $data) as $code) { + if (!isset($allcountries[$code])) { + return get_string('invalidcountrycode', 'core_error', $code); + } + } + + return true; + } +} + + /** * Selection of one of the recognised countries using the list * returned by {@link get_list_of_countries()}. diff --git a/lib/classes/string_manager_standard.php b/lib/classes/string_manager_standard.php index d34c09ea34c..1a3f36400c8 100644 --- a/lib/classes/string_manager_standard.php +++ b/lib/classes/string_manager_standard.php @@ -427,6 +427,7 @@ class core_string_manager_standard implements core_string_manager { $countries = $this->load_component_strings('core_countries', $lang); core_collator::asort($countries); + if (!$returnall and !empty($CFG->allcountrycodes)) { $enabled = explode(',', $CFG->allcountrycodes); $return = array(); @@ -435,7 +436,10 @@ class core_string_manager_standard implements core_string_manager { $return[$c] = $countries[$c]; } } - return $return; + + if (!empty($return)) { + return $return; + } } return $countries; diff --git a/lib/tests/string_manager_standard_test.php b/lib/tests/string_manager_standard_test.php index 54b48bbf034..aa73a0e6a9d 100644 --- a/lib/tests/string_manager_standard_test.php +++ b/lib/tests/string_manager_standard_test.php @@ -143,6 +143,57 @@ class core_string_manager_standard_testcase extends advanced_testcase { set_config('langlist', ''); get_string_manager(true); } + + /** + * Test {@see core_string_manager_standard::get_list_of_countries()} under different conditions. + */ + public function test_get_list_of_countries() { + + $this->resetAfterTest(); + $stringman = get_string_manager(); + + $countries = $stringman->get_list_of_countries(true); + $this->assertIsArray($countries); + $this->assertArrayHasKey('AU', $countries); + $this->assertArrayHasKey('BE', $countries); + $this->assertArrayHasKey('CZ', $countries); + $this->assertArrayHasKey('ES', $countries); + $this->assertGreaterThan(4, count($countries)); + + set_config('allcountrycodes', ''); + $countries = $stringman->get_list_of_countries(false); + $this->assertArrayHasKey('AU', $countries); + $this->assertArrayHasKey('BE', $countries); + $this->assertArrayHasKey('CZ', $countries); + $this->assertArrayHasKey('ES', $countries); + $this->assertGreaterThan(4, count($countries)); + + set_config('allcountrycodes', 'CZ,BE'); + $countries = $stringman->get_list_of_countries(true); + $this->assertArrayHasKey('AU', $countries); + $this->assertArrayHasKey('BE', $countries); + $this->assertArrayHasKey('CZ', $countries); + $this->assertArrayHasKey('ES', $countries); + $this->assertGreaterThan(4, count($countries)); + + $countries = $stringman->get_list_of_countries(false); + $this->assertEquals(2, count($countries)); + $this->assertArrayHasKey('BE', $countries); + $this->assertArrayHasKey('CZ', $countries); + + set_config('allcountrycodes', 'CZ,UVWXYZ'); + $countries = $stringman->get_list_of_countries(); + $this->assertArrayHasKey('CZ', $countries); + $this->assertEquals(1, count($countries)); + + set_config('allcountrycodes', 'UVWXYZ'); + $countries = $stringman->get_list_of_countries(); + $this->assertArrayHasKey('AU', $countries); + $this->assertArrayHasKey('BE', $countries); + $this->assertArrayHasKey('CZ', $countries); + $this->assertArrayHasKey('ES', $countries); + $this->assertGreaterThan(4, count($countries)); + } } /**