MDL-53240 filetypes: Introduce admin_setting_filetypes class

This new type of admin settings makes use of the filetypes browser but
for the admin settings.
This commit is contained in:
David Mudrák 2017-04-05 01:21:48 +02:00
parent 8b493eb09d
commit 8cf36e9c81
6 changed files with 237 additions and 6 deletions

View File

@ -0,0 +1,52 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_admin/setting_filetypes
Renders the admin_setting_filetypes setting element.
Context variables required for this template:
* id - element id
* name - form element name
* value - element value
* descriptions - data for the core_form/filetypes-descriptions template
Example context (json):
{
"id": "test0",
"name": "test",
"value": ".jpg,.gif",
"descriptions": {
"hasdescriptions": true,
"descriptions": [
{
"description": "Image (JPEG)",
"extensions": ".jpeg .jpe .jpg"
},
{
"description": "Image (GIF)",
"extensions": ".gif"
}
]
}
}
}}
<div class="form-text defaultsnext">
<input type="text" name="{{name}}" value="{{value}}" size="30" id="{{id}}" class="text-ltr">
<span data-filetypesbrowser="{{id}}"></span>
<div data-filetypesdescriptions="{{id}}">{{#descriptions}}{{>core_form/filetypes-descriptions}}{{/descriptions}}</div>
</div>

View File

@ -42,6 +42,7 @@ $string['err_numeric'] = 'You must enter a number here.';
$string['err_rangelength'] = 'You must enter between {$a->format[0]} and {$a->format[1]} characters here.';
$string['err_required'] = 'You must supply a value here.';
$string['filetypesany'] = 'All file types';
$string['filetypesnotwhitelisted'] = 'These file types are not allowed here: {$a}';
$string['filetypesothers'] = 'Other files';
$string['general'] = 'General';
$string['hideadvanced'] = 'Hide advanced';

View File

@ -10519,3 +10519,121 @@ class admin_setting_scsscode extends admin_setting_configtextarea {
return true;
}
}
/**
* Administration setting to define a list of file types.
*
* @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
* @copyright 2017 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class admin_setting_filetypes extends admin_setting_configtext {
/** @var array Allow selection from these file types only. */
protected $onlytypes = [];
/** @var bool Allow selection of 'All file types' (will be stored as '*'). */
protected $allowall = true;
/** @var core_form\filetypes_util instance to use as a helper. */
protected $util = null;
/**
* Constructor.
*
* @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
* @param string $visiblename Localised label of the setting
* @param string $description Localised description of the setting
* @param string $defaultsetting Default setting value.
* @param array $options Setting widget options, an array with optional keys:
* 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
* 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
*/
public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
$this->onlytypes = $options['onlytypes'];
}
if (!$this->onlytypes && array_key_exists('allowall', $options)) {
$this->allowall = (bool)$options['allowall'];
}
$this->util = new \core_form\filetypes_util();
}
/**
* Normalize the user's input and write it to the database as comma separated list.
*
* Comma separated list as a text representation of the array was chosen to
* make this compatible with how the $CFG->courseoverviewfilesext values are stored.
*
* @param string $data Value submitted by the admin.
* @return string Epty string if all good, error message otherwise.
*/
public function write_setting($data) {
return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
}
/**
* Validate data before storage
*
* @param string $data The setting values provided by the admin
* @return bool|string True if ok, the string if error found
*/
public function validate($data) {
// No need to call parent's validation here as we are PARAM_RAW.
if ($this->util->is_whitelisted($data, $this->onlytypes)) {
return true;
} else {
$troublemakers = $this->util->get_not_whitelisted($data, $this->onlytypes);
return get_string('filetypesnotwhitelisted', 'core_form', implode(' ', $troublemakers));
}
}
/**
* Return an HTML string for the setting element.
*
* @param string $data The current setting value
* @param string $query Admin search query to be highlighted
* @return string HTML to be displayed
*/
public function output_html($data, $query='') {
global $OUTPUT, $PAGE;
$default = $this->get_defaultsetting();
$context = (object) [
'id' => $this->get_id(),
'name' => $this->get_full_name(),
'value' => $data,
'descriptions' => $this->util->describe_file_types($data),
];
$element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
$PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
$this->get_id(),
$this->visiblename,
$this->onlytypes,
$this->allowall,
]);
return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
}
/**
* Should the values be always displayed in LTR mode?
*
* We always return true here because these values are not RTL compatible.
*
* @return bool True because these values are not RTL compatible.
*/
public function get_force_ltr() {
return true;
}
}

View File

@ -414,25 +414,42 @@ class filetypes_util {
* Should the given file type be considered as a part of the given whitelist.
*
* If multiple types are provided, all of them must be part of the
* whitelist.
* whitelist. Empty type is part of any whitelist. Any type is part of an
* empty whitelist.
*
* @param string $types One or more types in a string (space , or ; separated)
* @param string|array $whitelist an array or string of whitelisted types
* @param string|array $types File types to be checked
* @param string|array $whitelist An array or string of whitelisted types
* @return boolean
*/
public function is_whitelisted($types, $whitelist) {
return empty($this->get_not_whitelisted($types, $whitelist));
}
/**
* Returns all types that are not part of the give whitelist.
*
* This is similar check to the {@link self::is_whitelisted()} but this one
* actually returns the extra types.
*
* @param string|array $types File types to be checked
* @param string|array $whitelist An array or string of whitelisted types
* @return array Types not present in the whitelist
*/
public function get_not_whitelisted($types, $whitelist) {
$whitelistedtypes = $this->expand($whitelist, true, true);
if (empty($whitelistedtypes) || $whitelistedtypes == ['*']) {
return true;
return [];
}
$giventypes = $this->normalize_file_types($types);
$intersection = array_intersect($giventypes, $whitelistedtypes);
if (empty($giventypes)) {
return [];
}
return !empty($intersection);
return array_diff($giventypes, $whitelistedtypes);
}
/**

View File

@ -219,6 +219,7 @@ class filetypes_util_testcase extends advanced_testcase {
$this->assertTrue($util->is_whitelisted('audio', 'text/plain audio video'));
$this->assertTrue($util->is_whitelisted('text/plain', 'text/plain audio video'));
$this->assertTrue($util->is_whitelisted('jpg jpe jpeg', 'image/jpeg'));
$this->assertTrue($util->is_whitelisted(['jpg', 'jpe', '.png'], 'image'));
// These should be intuitively false.
$this->assertFalse($util->is_whitelisted('.gif', 'text/plain'));
@ -229,9 +230,44 @@ class filetypes_util_testcase extends advanced_testcase {
// Not all documents (and also the group itself) is not a plain text.
$this->assertFalse($util->is_whitelisted('document', 'text/plain'));
// This may look wrong at the first sight as you might expect that the
// mimetype should simply map to an extension ...
$this->assertFalse($util->is_whitelisted('image/jpeg', '.jpg'));
// But it is principally same situation as this (there is no 1:1 mapping).
$this->assertFalse($util->is_whitelisted('.c', '.txt'));
$this->assertTrue($util->is_whitelisted('.txt .c', 'text/plain'));
$this->assertFalse($util->is_whitelisted('text/plain', '.c'));
// Any type is included if the filter is empty.
$this->assertTrue($util->is_whitelisted('txt', ''));
$this->assertTrue($util->is_whitelisted('txt', '*'));
// Empty value is part of any whitelist.
$this->assertTrue($util->is_whitelisted('', '.txt'));
}
/**
* Test getting types not present in a whitelist.
*/
public function test_get_not_whitelisted() {
$this->resetAfterTest(true);
$util = new filetypes_util();
$this->assertEmpty($util->get_not_whitelisted('txt', 'text/plain'));
$this->assertEmpty($util->get_not_whitelisted('txt', '.doc .txt .rtf'));
$this->assertEmpty($util->get_not_whitelisted('txt', 'text/plain'));
$this->assertEmpty($util->get_not_whitelisted(['jpg', 'jpe', 'jpeg'], 'image/jpeg'));
$this->assertEmpty($util->get_not_whitelisted('', 'foo/bar'));
$this->assertEmpty($util->get_not_whitelisted('.foobar', ''));
$this->assertEmpty($util->get_not_whitelisted('.foobar', '*'));
// Returned list is normalized so extensions have the dot added.
$this->assertContains('.exe', $util->get_not_whitelisted('exe', '.c .h'));
// If this looks wrong to you, see {@link test_is_whitelisted()} for more details on this behaviour.
$this->assertContains('image/jpeg', $util->get_not_whitelisted('image/jpeg', '.jpg .jpeg'));
}
/**

View File

@ -1,6 +1,13 @@
This files describes API changes in core libraries and APIs,
information provided here is intended especially for developers.
=== 3.4 ===
* Added new moodleform element 'filetypes' and new admin setting widget 'admin_setting_filetypes'. These new widgets
allow users to define a list of file types; either by typing them manually or selecting them from a list. The widgets
directly support the syntax used to feed the 'accepted_types' option of the filemanager and filepicker elements. File
types can be specified as extensions (.jpg or just jpg), mime types (text/plain) or groups (image).
=== 3.3.1 ===
* ldap_get_entries_moodle() now always returns lower-cased attribute names in the returned entries.