--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Generates a secure key for the current server (presuming it does not already exist).
+ *
+ * @package core_admin
+ * @copyright 2020 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+use \core\encryption;
+
+define('CLI_SCRIPT', true);
+
+require(__DIR__ . '/../../config.php');
+require_once($CFG->libdir . '/clilib.php');
+
+// Get cli options.
+[$options, $unrecognized] = cli_get_params(
+ ['help' => false, 'method' => null],
+ ['h' => 'help']);
+
+if ($unrecognized) {
+ $unrecognized = implode("\n ", $unrecognized);
+ cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
+}
+
+if ($options['help']) {
+ echo "Generate secure key
+
+This script manually creates a secure key within the secret data root folder (configured in
+config.php as \$CFG->secretdataroot). You must run it using an account with access to write
+to that folder.
+
+In normal use Moodle automatically creates the key; this script is intended when setting up
+a new Moodle system, for cases where the secure folder is not on shared storage and the key
+may be manually installed on multiple servers.
+
+Options:
+-h, --help Print out this help
+--method <method> Generate key for specified encryption method instead of default.
+ * sodium
+ * openssl-aes-256-ctr
+
+Example:
+php admin/cli/generate_key.php
+";
+ exit;
+}
+
+$method = $options['method'];
+
+if (encryption::key_exists($method)) {
+ echo 'Key already exists: ' . encryption::get_key_file($method) . "\n";
+ exit;
+}
+
+// Creates key with default permissions (no chmod).
+echo "Generating key...\n";
+encryption::create_key($method, false);
+
+echo "\nKey created: " . encryption::get_key_file($method) . "\n\n";
+echo "If the key folder is not shared storage, then key files should be copied to all servers.\n";
$settings->add(new admin_setting_configduration('analytics/modeltimelimit', new lang_string('modeltimelimit', 'analytics'),
new lang_string('modeltimelimitinfo', 'analytics'), 20 * MINSECS));
+ $options = array(
+ 0 => new lang_string('neverdelete', 'analytics'),
+ 1000 => new lang_string('numdays', '', 1000),
+ 365 => new lang_string('numdays', '', 365),
+ 180 => new lang_string('numdays', '', 180),
+ 150 => new lang_string('numdays', '', 150),
+ 120 => new lang_string('numdays', '', 120),
+ 90 => new lang_string('numdays', '', 90),
+ 60 => new lang_string('numdays', '', 60),
+ 35 => new lang_string('numdays', '', 35));
+ $settings->add(new admin_setting_configselect('analytics/calclifetime',
+ new lang_string('calclifetime', 'analytics'),
+ new lang_string('configlcalclifetime', 'analytics'), 35, $options));
+
+
}
}
$temp->add(new admin_setting_configduration('sessiontimeout', new lang_string('sessiontimeout', 'admin'),
new lang_string('configsessiontimeout', 'admin'), 8 * 60 * 60));
+ $sessiontimeoutwarning = new admin_setting_configduration('sessiontimeoutwarning',
+ new lang_string('sessiontimeoutwarning', 'admin'),
+ new lang_string('configsessiontimeoutwarning', 'admin'), 20 * 60);
+
+ $sessiontimeoutwarning->set_validate_function(function(int $value): string {
+ global $CFG;
+ // Check sessiontimeoutwarning is less than sessiontimeout.
+ if ($CFG->sessiontimeout <= $value) {
+ return get_string('configsessiontimeoutwarningcheck', 'admin');
+ } else {
+ return '';
+ }
+ });
+
+ $temp->add($sessiontimeoutwarning);
+
$temp->add(new admin_setting_configtext('sessioncookie', new lang_string('sessioncookie', 'admin'),
new lang_string('configsessioncookie', 'admin'), '', PARAM_ALPHANUM));
$temp->add(new admin_setting_configtext('sessioncookiepath', new lang_string('sessioncookiepath', 'admin'),
$pageurl = new \moodle_url('/admin/tasklogs.php');
$pageurl->param('filter', $filter);
+$pageurl->param('result', $result);
$PAGE->set_url($pageurl);
$PAGE->set_context(context_system::instance());
--- /dev/null
+{{!
+ 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/admin_setting_encryptedpassword
+
+ Admin encrypted password template.
+
+ Context variables required for this template:
+ * name - form element name
+ * set - whether it is set or empty
+ * id - element id
+
+ Example context (json):
+ {
+ "name": "test",
+ "id": "test0",
+ "set": true
+ }
+}}
+<div class="core_admin_encryptedpassword" data-encryptedpasswordid="{{ id }}"
+ {{#novalue}}data-novalue="y"{{/novalue}}>
+ {{#set}}
+ <span>{{# str }} encryptedpassword_set, admin {{/ str }}</span>
+ {{/set}}
+ {{^set}}
+ <a href="#" title="{{# str }} encryptedpassword_edit, admin {{/ str }}">
+ <span>{{# str }} novalueclicktoset, form {{/ str }}</span>
+ {{# pix }} t/passwordunmask-edit, core, {{# str }} passwordunmaskedithint, form {{/ str }}{{/ pix }}
+ </a>
+ {{/set}}
+ <input style="display: none" type="password" name="{{name}}" disabled>
+ {{!
+ Using buttons instead of links here allows them to be connected to the label, so the button
+ works if you click the label.
+ }}
+ {{#set}}
+ <button type="button" id="{{id}}" title="{{# str }} encryptedpassword_edit, admin {{/ str }}" class="btn btn-link" data-editbutton>
+ {{# pix }} t/passwordunmask-edit, core, {{/ pix }}
+ </button>
+ {{/set}}
+ <button type="button" style="display: none" title="{{# str }} cancel {{/ str }}" class="btn btn-link" data-cancelbutton>
+ <i class="icon fa fa-times"></i>
+ </button>
+</div>
+
+{{#js}}
+require(['core_form/encryptedpassword'], function(encryptedpassword) {
+ new encryptedpassword.EncryptedPassword("{{ id }}");
+});
+{{/js}}
}
/**
- * Sets the specified site settings. A table with | config | value | (optional)plugin | is expected.
+ * Sets the specified site settings. A table with | config | value | (optional)plugin | (optional)encrypted | is expected.
*
* @Given /^the following config values are set as admin:$/
* @param TableNode $table
foreach ($data as $config => $value) {
// Default plugin value is null.
$plugin = null;
+ $encrypted = false;
if (is_array($value)) {
$plugin = $value[1];
+ if (array_key_exists(2, $value)) {
+ $encrypted = $value[2] === 'encrypted';
+ }
$value = $value[0];
}
+
+ if ($encrypted) {
+ $value = \core\encryption::encrypt($value);
+ }
+
set_config($config, $value, $plugin);
}
}
Background:
Given the following "users" exist:
- | username | firstname | lastname | email |
- | teacher1 | Teacher | 1 | teacher1@example.com |
+ | username | firstname | lastname |
+ | teacher1 | Teacher | 1 |
+ | tutor | Teaching | Assistant |
+ | student | Student | One |
And the following "courses" exist:
- | fullname | shortname | category |
- | Course 1 | C1 | 0 |
+ | fullname | shortname |
+ | Course 1 | C1 |
And the following "course enrolments" exist:
- | user | course | role |
- | teacher1 | C1 | editingteacher |
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | tutor | C1 | teacher |
+ | student | C1 | student |
Scenario: Default system capabilities modification
Given I log in as "admin"
Then "mod/forum:deleteanypost" capability has "Prohibit" permission
And "mod/forum:editanypost" capability has "Prevent" permission
And "mod/forum:addquestion" capability has "Allow" permission
+
+ @javascript
+ Scenario: Edit permissions escapes role names correctly
+ When I am on the "C1" "Course" page logged in as "admin"
+ And I navigate to "Edit settings" in current page administration
+ And I set the following fields to these values:
+ | Your word for 'Teacher' | Teacher >= editing |
+ | Your word for 'Non-editing teacher' | Teacher < "editing" |
+ | Your word for 'Student' | Studier & 'learner' |
+ And I press "Save and display"
+ And I navigate to course participants
+ Then I should see "Teacher >= editing (Teacher)" in the "Teacher 1" "table_row"
+ And I should see "Teacher < \"editing\" (Non-editing teacher)" in the "Teaching Assistant" "table_row"
+ And I should see "Studier & 'learner' (Student)" in the "Student One" "table_row"
+ And I navigate to "Users > Permissions" in current page administration
+ And I should see "Teacher >= editing" in the "mod/forum:replypost" "table_row"
+ And I should see "Teacher < \"editing\"" in the "mod/forum:replypost" "table_row"
+ And I should see "Studier & 'learner'" in the "mod/forum:replypost" "table_row"
+ And I follow "Prohibit"
+ And "Teacher >= editing" "button" in the "Prohibit role" "dialogue" should be visible
+ And "Teacher < \"editing\"" "button" in the "Prohibit role" "dialogue" should be visible
+ And "Studier & 'learner'" "button" in the "Prohibit role" "dialogue" should be visible
And I press the shift tab key
And the focused element is "Username" "field"
- @javascript
- Scenario: Using the arrow keys allows me to navigate through menus
- Given the following "users" exist:
- | username | email | firstname | lastname |
- | saffronr | saffron.rutledge@example.com | Saffron | Rutledge |
- And I log in as "saffronr"
- And I click on "Saffron Rutledge" "link" in the ".usermenu" "css_element"
- When I press the up key
- Then the focused element is "Log out" "link"
+# TODO: Uncomment the following when MDL-66979 is integrated.
+# @javascript
+# Scenario: Using the arrow keys allows me to navigate through menus
+# Given the following "users" exist:
+# | username | email | firstname | lastname |
+# | saffronr | saffron.rutledge@example.com | Saffron | Rutledge |
+# And I log in as "saffronr"
+# And I click on "Saffron Rutledge" "link" in the ".usermenu" "css_element"
+# When I press the up key
+# Then the focused element is "Log out" "link"
@javascript
Scenario: The escape key can be used to close a dialogue
And I open the action menu in "region-main" "region"
And I choose "Purposes" in the open action menu
And I press "Add purpose"
- And I set the field "Name" to "Purpose 1"
- And I set the field "Description" to "Purpose 1 description"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Lawful bases" "form_row"
- And I click on "Contract (GDPR Art. 6.1(b))" "list_item"
- And I click on "Legal obligation (GDPR Art 6.1(c))" "list_item"
- And I press the escape key
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Sensitive personal data processing reasons" "form_row"
- And I click on "Explicit consent (GDPR Art. 9.2(a))" "list_item"
- And I press the escape key
- And I set the field "retentionperiodnumber" to "2"
+ And I set the following fields to these values:
+ | Name | Purpose 1 |
+ | Description | Purpose 1 description |
+ | Lawful bases | Contract (GDPR Art. 6.1(b)),Legal obligation (GDPR Art 6.1(c)) |
+ | Sensitive personal data processing reasons | Explicit consent (GDPR Art. 9.2(a)) |
+ | retentionperiodnumber | 2 |
When I press "Save"
Then I should see "Purpose 1" in the "List of data purposes" "table"
And I should see "Contract (GDPR Art. 6.1(b))" in the "Purpose 1" "table_row"
Scenario: Update a data storage purpose
Given I open the action menu in "Purpose 1" "table_row"
And I choose "Edit" in the open action menu
- And I set the field "Name" to "Purpose 1 edited"
- And I set the field "Description" to "Purpose 1 description edited"
- And I click on "Legal obligation (GDPR Art 6.1(c))" "text" in the ".form-autocomplete-selection" "css_element"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Lawful bases" "form_row"
- And I click on "Vital interests (GDPR Art. 6.1(d))" "list_item"
- And I press the escape key
- And I set the field "retentionperiodnumber" to "3"
- And I click on "protected" "checkbox"
+ And I set the following fields to these values:
+ | Name | Purpose 1 edited |
+ | Description | Purpose 1 description edited |
+ | Lawful bases | Contract (GDPR Art. 6.1(b)), Vital interests (GDPR Art. 6.1(d)) |
+ | Sensitive personal data processing reasons | Explicit consent (GDPR Art. 9.2(a)) |
+ | retentionperiodnumber | 3 |
+ | protected | 1 |
When I press "Save changes"
Then I should see "Purpose 1 edited" in the "List of data purposes" "table"
And I should see "Purpose 1 description edited" in the "Purpose 1 edited" "table_row"
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_langimport\output;
-defined('MOODLE_INTERNAL') || die();
+use core_collator;
use moodle_url;
use renderable;
use renderer_base;
if (!empty($this->availablelanguages)) {
$data->toinstalloptions = [];
+
+ core_collator::asort($this->availablelanguages);
foreach ($this->availablelanguages as $code => $language) {
$option = new stdClass();
$option->value = $code;
And I follow "Home"
And I navigate to "Competencies > Learning plan templates" in site administration
And I click on ".template-cohorts" "css_element" in the "Science template cohort" "table_row"
- And I click on ".form-autocomplete-downarrow" "css_element"
- And I click on "cohort plan" item in the autocomplete list
- And I press the escape key
+ And I set the field "Select cohorts to sync" to "cohort plan"
When I click on "Add cohorts" "button"
Then I should see "2 learning plans were created."
And I follow "Learning plan templates"
} else {
$('[data-region="list-templates"] [data-action="clearsearch"]').addClass('d-none');
}
- // Trigger the search.
- document.location.hash = searchStr;
+ // Trigger the search.
ajax.call([
{methodname: 'tool_templatelibrary_list_templates',
args: {component: componentStr, search: searchStr, themename: themename},
$(this).addClass('d-none');
});
- $('[data-region="input"]').val(document.location.hash.replace('#', ''));
refreshSearch(config.theme);
return {};
});
*/
class list_templates_page implements renderable, templatable {
+ /** @var string $component The currently selected component */
+ protected $component;
+ /** @var string $search The current search */
+ protected $search;
+
+ /**
+ * Template page constructor
+ *
+ * @param string $component
+ * @param string $search
+ */
+ public function __construct(string $component = '', string $search = '') {
+ $this->component = $component;
+ $this->search = $search;
+ }
+
/**
* Export this data so it can be used as the context for a mustache template.
*
$components[$type]->plugins[$component] = (object) [
'name' => $pluginname,
'component' => $component,
+ 'selected' => ($component === $this->component),
];
}
return (object) [
'allcomponents' => array_values($components),
+ 'search' => $this->search,
];
}
}
{{/label}}
{{$element}}
- <select id="selectcomponent" class="form-control" data-field="component">
+ <select id="selectcomponent" name="component" class="form-control" data-field="component">
<option value="">{{#str}}all, tool_templatelibrary{{/str}}</option>
{{#allcomponents}}
<optgroup label="{{type}}">
{{#plugins}}
- <option value="{{component}}">{{name}}</option>
+ <option value="{{component}}" {{#selected}}selected{{/selected}}>{{name}}</option>
{{/plugins}}
</optgroup>
{{/allcomponents}}
{{< core_form/element-template }}
{{$element}}
{{< core/search_input_auto }}
- {{$label}}{{{ searchstring }}}{{/label}}
- {{$placeholder}}{{#str}}
- search, core
- {{/str}}{{/placeholder}}
+ {{$label}}
+ {{#str}} search, tool_templatelibrary {{/str}}
+ {{/label}}
+ {{$value}}{{ search }}{{/value}}
{{/ core/search_input_auto }}
{{/element}}
{{/ core_form/element-template }}
This files describes API changes in /admin/*.
+=== 3.11 ===
+
+* New admin setting admin_setting_encryptedpassword allows passwords in admin settings to be
+ encrypted (with the new \core\encryption API) so that even the admin cannot read them.
+
=== 3.9 ===
* The following functions, previously used (exclusively) by upgrade steps are not available anymore because of the upgrade cleanup performed for this version. See MDL-65809 for more info:
}
/**
- * Returns the list of analysable elements available on the site.
- *
- * \core_analytics\local\analyser\by_course and \core_analytics\local\analyser\sitewide are implementing
- * this method returning site courses (by_course) and the whole system (sitewide) as analysables.
- *
- * @todo MDL-65284 This will be removed in Moodle 3.11
- * @deprecated
- * @see get_analysables_iterator
- * @throws \coding_exception
- * @return \core_analytics\analysable[] Array of analysable elements using the analysable id as array key.
+ * @deprecated since Moodle 3.7
*/
public function get_analysables() {
- // This function should only be called from get_analysables_iterator and we keep it here until Moodle 3.11
- // for backwards compatibility.
- throw new \coding_exception('This method is deprecated in favour of get_analysables_iterator.');
+ throw new \coding_exception('get_analysables() method has been removed and cannot be used any more.');
}
/**
* @param \context[] $contexts Only analysables that depend on the provided contexts. All analysables in the system if empty.
* @return \Iterator
*/
- public function get_analysables_iterator(?string $action = null, array $contexts = []) {
-
- debugging('Please overwrite get_analysables_iterator with your own implementation, we only keep this default
- implementation for backwards compatibility purposes with get_analysables(). note that $action param will
- be ignored so the analysable elements will be processed using get_analysables order, regardless of the
- last time they were processed.');
-
- return new \ArrayIterator($this->get_analysables());
- }
+ abstract public function get_analysables_iterator(?string $action = null, array $contexts = []);
/**
* This function returns this analysable list of samples.
}
/**
- * Returns the enabled time splitting methods.
- *
- * @deprecated since Moodle 3.7
- * @todo MDL-65086 This will be deleted in Moodle 3.11
- * @see \core_analytics\manager::get_time_splitting_methods_for_evaluation
- * @return \core_analytics\local\time_splitting\base[]
+ * @deprecated since Moodle 3.7 use get_time_splitting_methods_for_evaluation instead
*/
public static function get_enabled_time_splitting_methods() {
- debugging('This function has been deprecated. You can use self::get_time_splitting_methods_for_evaluation if ' .
+ throw new coding_exception(__FUNCTION__ . '() has been removed. You can use self::get_time_splitting_methods_for_evaluation if ' .
'you want to get the default time splitting methods for evaluation, or you can use self::get_all_time_splittings if ' .
'you want to get all the time splitting methods available on this site.');
- return self::get_time_splitting_methods_for_evaluation();
}
/**
*/
public static function add_builtin_models() {
- debugging('core_analytics\manager::add_builtin_models() has been deprecated. Core models are now automatically '.
- 'updated according to their declaration in the lib/db/analytics.php file.', DEBUG_DEVELOPER);
+ throw new \coding_exception('core_analytics\manager::add_builtin_models() has been removed. Core models ' .
+ 'are now automatically updated according to their declaration in the lib/db/analytics.php file.');
}
/**
$param + $idsparams);
}
}
+
+ // Clean up calculations table.
+ $calclifetime = get_config('analytics', 'calclifetime');
+ if (!empty($calclifetime)) {
+ $lifetime = time() - ($calclifetime * DAYSECS); // Value in days.
+ $DB->delete_records_select('analytics_indicator_calc', 'timecreated < ?', [$lifetime]);
+ }
}
/**
And I choose "Create model" in the open action menu
And I set the field "Enabled" to "Enable"
And I select "__core_course__analytics__target__course_completion" from the "target" singleselect
- And I open the autocomplete suggestions list
- And I click on "Read actions amount" item in the autocomplete list
- And I open the autocomplete suggestions list
- And I click on "Any write action in the course" item in the autocomplete list
+ And I set the field "Indicators" to "Read actions amount, Any write action in the course"
And I select "__core__analytics__time_splitting__single_range" from the "timesplitting" singleselect
And I press "Save changes"
Then I should see "No predictions available yet" in the "Students at risk of not meeting the course completion conditions" "table_row"
This files describes API changes in analytics sub system,
information provided here is intended especially for developers.
+=== 3.11 ===
+
+* Final deprecation get_enabled_time_splitting_methods. Method has been removed. Use
+ get_time_splitting_methods_for_evaluation instead.
+* Final deprecation add_builtin_models. Method has been removed. The functionality
+ has been replaced with automatic update of models provided by the core moodle component.
+ There is no need to call this method explicitly any more. Instead, adding new models can be achieved
+ by updating the lib/db/analytics.php file and bumping the core version.
+* Final deprecation - get_analysables(). Please see get_analysables_interator() instead.
+ get_analysables_iterator() needs to be overridden by the child class.
+
=== 3.8 ===
* "Time-splitting method" have been replaced by "Analysis interval" for the language strings that are
$newcm = duplicate_module($course, get_fast_modinfo($course)->get_cm($quiz->cmid));
- $sql = "SELECT qa.answer
+ $sql = "SELECT qa.id, qa.answer
FROM {quiz} q
LEFT JOIN {quiz_slots} qs ON qs.quizid = q.id
LEFT JOIN {question_answers} qa ON qa.question = qs.questionid
WHERE q.id = :quizid";
$params = array('quizid' => $newcm->instance);
- $answers = $DB->get_fieldset_sql($sql, $params);
- $this->assertEquals($CFG->wwwroot . '/course/view.php?id=' . $course->id, $answers[0]);
- $this->assertEquals($CFG->wwwroot . '/mod/quiz/view.php?id=' . $quiz->cmid, $answers[1]);
- $this->assertEquals($CFG->wwwroot . '/grade/report/index.php?id=' . $quiz->cmid, $answers[2]);
- $this->assertEquals($CFG->wwwroot . '/mod/quiz/index.php?id=' . $quiz->cmid, $answers[3]);
+ $answers = $DB->get_records_sql_menu($sql, $params);
+
+ $this->assertEquals($CFG->wwwroot . '/course/view.php?id=' . $course->id, $answers[$firstanswer->id]);
+ $this->assertEquals($CFG->wwwroot . '/mod/quiz/view.php?id=' . $quiz->cmid, $answers[$secondanswer->id]);
+ $this->assertEquals($CFG->wwwroot . '/grade/report/index.php?id=' . $quiz->cmid, $answers[$thirdanswer->id]);
+ $this->assertEquals($CFG->wwwroot . '/mod/quiz/index.php?id=' . $quiz->cmid, $answers[$fourthanswer->id]);
}
}
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * This is the external method used for fetching the addable blocks in a given page.
+ *
+ * @package core_block
+ * @since Moodle 3.11
+ * @copyright 2020 Mihail Geshoski <mihail@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_block\external;
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/externallib.php');
+
+use external_api;
+use external_function_parameters;
+use external_multiple_structure;
+use external_single_structure;
+use external_value;
+
+/**
+ * This is the external method used for fetching the addable blocks in a given page.
+ *
+ * @copyright 2020 Mihail Geshoski <mihail@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class fetch_addable_blocks extends external_api {
+
+ /**
+ * Describes the parameters for execute.
+ *
+ * @return external_function_parameters
+ */
+ public static function execute_parameters(): external_function_parameters {
+ return new external_function_parameters(
+ [
+ 'pagecontextid' => new external_value(PARAM_INT, 'The context ID of the page.'),
+ 'pagetype' => new external_value(PARAM_ALPHAEXT, 'The type of the page.'),
+ 'pagelayout' => new external_value(PARAM_ALPHA, 'The layout of the page.')
+ ]
+ );
+ }
+
+ /**
+ * Fetch the addable blocks in a given page.
+ *
+ * @param int $pagecontextid The context ID of the page
+ * @param string $pagetype The type of the page
+ * @param string $pagelayout The layout of the page
+ * @return array The blocks list
+ */
+ public static function execute(int $pagecontextid, string $pagetype, string $pagelayout): array {
+ global $PAGE;
+
+ $params = self::validate_parameters(self::execute_parameters(),
+ [
+ 'pagecontextid' => $pagecontextid,
+ 'pagetype' => $pagetype,
+ 'pagelayout' => $pagelayout
+ ]
+ );
+
+ $context = \context::instance_by_id($params['pagecontextid']);
+ // Validate the context. This will also set the context in $PAGE.
+ self::validate_context($context);
+
+ // We need to manually set the page layout and page type.
+ $PAGE->set_pagelayout($params['pagelayout']);
+ $PAGE->set_pagetype($params['pagetype']);
+ // Firstly, we need to load all currently existing page blocks to later determine which blocks are addable.
+ $PAGE->blocks->load_blocks(false);
+ $PAGE->blocks->create_all_block_instances();
+
+ $addableblocks = $PAGE->blocks->get_addable_blocks();
+
+ return array_map(function($block) {
+ return [
+ 'name' => $block->name,
+ 'title' => get_string('pluginname', "block_{$block->name}")
+ ];
+ }, $addableblocks);
+ }
+
+ /**
+ * Describes the execute return value.
+ *
+ * @return external_multiple_structure
+ */
+ public static function execute_returns(): external_multiple_structure {
+ return new external_multiple_structure(
+ new external_single_structure(
+ [
+ 'name' => new external_value(PARAM_PLUGIN, 'The name of the block.'),
+ 'title' => new external_value(PARAM_RAW, 'The title of the block.'),
+ ]
+ ),
+ 'List of addable blocks in a given page.'
+ );
+ }
+}
// Filter out all pagination options which are too large for the amount of courses user is enrolled in.
var totalCourseCount = parseInt(root.find(Selectors.courseView.region).attr('data-totalcoursecount'), 10);
- if (totalCourseCount) {
- itemsPerPage = itemsPerPage.filter(function(pagingOption) {
- return pagingOption.value < totalCourseCount;
- });
- }
+ itemsPerPage = itemsPerPage.filter(function(pagingOption) {
+ return pagingOption.value < totalCourseCount || pagingOption.value === 0;
+ });
var filters = getFilterValues(root);
var config = $.extend({}, DEFAULT_PAGED_CONTENT_CONFIG);
pageCourses = $.merge(loadedPages[currentPage].courses, courses.slice(0, nextPageStart));
}
} else {
- nextPageStart = pageData.limit;
+ // When the page limit is zero, there is only one page of courses, no start for next page.
+ nextPageStart = pageData.limit || false;
pageCourses = (pageData.limit > 0) ? courses.slice(0, pageData.limit) : courses;
}
courses: pageCourses
};
- // Set up the next page
- var remainingCourses = nextPageStart ? courses.slice(nextPageStart, courses.length) : [];
+ // Set up the next page (if there is more than one page).
+ var remainingCourses = nextPageStart !== false ? courses.slice(nextPageStart, courses.length) : [];
if (remainingCourses.length) {
loadedPages[currentPage + 1] = {
courses: remainingCourses
// Check and remember the given view.
$this->view = $view ? $view : BLOCK_MYOVERVIEW_VIEW_CARD;
- // Check and remember the given page size.
- if ($paging == BLOCK_MYOVERVIEW_PAGING_ALL) {
+ // Check and remember the given page size, `null` indicates no page size set
+ // while a `0` indicates a paging size of `All`.
+ if (!is_null($paging) && $paging == BLOCK_MYOVERVIEW_PAGING_ALL) {
$this->paging = BLOCK_MYOVERVIEW_PAGING_ALL;
} else {
$this->paging = $paging ? $paging : BLOCK_MYOVERVIEW_PAGING_12;
| student1 | Student | X | student1@example.com | S1 |
And the following "courses" exist:
| fullname | shortname | category |
- | Course 1 | C1 | 0 |
- | Course 2 | C2 | 0 |
- | Course 3 | C3 | 0 |
- | Course 4 | C4 | 0 |
- | Course 5 | C5 | 0 |
- | Course 6 | C6 | 0 |
- | Course 7 | C7 | 0 |
- | Course 8 | C8 | 0 |
- | Course 9 | C9 | 0 |
+ | Course 1 | C01 | 0 |
+ | Course 2 | C02 | 0 |
+ | Course 3 | C03 | 0 |
+ | Course 4 | C04 | 0 |
+ | Course 5 | C05 | 0 |
+ | Course 6 | C06 | 0 |
+ | Course 7 | C07 | 0 |
+ | Course 8 | C08 | 0 |
+ | Course 9 | C09 | 0 |
| Course 10 | C10 | 0 |
| Course 11 | C11 | 0 |
| Course 12 | C12 | 0 |
| Course 13 | C13 | 0 |
And the following "course enrolments" exist:
| user | course | role |
- | student1 | C1 | student |
- | student1 | C2 | student |
- | student1 | C3 | student |
- | student1 | C4 | student |
- | student1 | C5 | student |
- | student1 | C6 | student |
- | student1 | C7 | student |
- | student1 | C8 | student |
- | student1 | C9 | student |
+ | student1 | C01 | student |
+ | student1 | C02 | student |
+ | student1 | C03 | student |
+ | student1 | C04 | student |
+ | student1 | C05 | student |
+ | student1 | C06 | student |
+ | student1 | C07 | student |
+ | student1 | C08 | student |
+ | student1 | C09 | student |
| student1 | C10 | student |
| student1 | C11 | student |
| student1 | C12 | student |
Scenario: Toggle the page limit between page reloads
Given I log in as "student1"
- When I click on "[data-toggle='dropdown']" "css_element" in the "Course overview" "block"
- And I click on "All" "link"
+ When I click on "[data-action='limit-toggle']" "css_element" in the "Course overview" "block"
+ And I click on "All" "link" in the ".dropdown-menu.show" "css_element"
Then I should see "Course 13"
And I reload the page
Then I should see "Course 13"
Scenario: Toggle the page limit between grouping changes
Given I log in as "student1"
- When I click on "[data-toggle='dropdown']" "css_element" in the "Course overview" "block"
- And I click on "All" "link"
+ When I click on "[data-action='limit-toggle']" "css_element" in the "Course overview" "block"
+ And I click on "All" "link" in the ".dropdown-menu.show" "css_element"
And I click on "All (except removed from view)" "button" in the "Course overview" "block"
And I click on "In progress" "link" in the "Course overview" "block"
Then I should see "Course 13"
--- /dev/null
+@block @block_myoverview @javascript
+Feature: My overview block pagination
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email | idnumber |
+ | student1 | Student | X | student1@example.com | S1 |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 01 | C1 | 0 |
+ | Course 02 | C2 | 0 |
+ | Course 03 | C3 | 0 |
+ | Course 04 | C4 | 0 |
+ | Course 05 | C5 | 0 |
+ | Course 06 | C6 | 0 |
+ | Course 07 | C7 | 0 |
+ | Course 08 | C8 | 0 |
+ | Course 09 | C9 | 0 |
+ | Course 10 | C10 | 0 |
+ | Course 11 | C11 | 0 |
+ | Course 12 | C12 | 0 |
+ | Course 13 | C13 | 0 |
+ | Course 14 | C14 | 0 |
+ | Course 15 | C15 | 0 |
+ | Course 16 | C16 | 0 |
+ | Course 17 | C17 | 0 |
+ | Course 18 | C18 | 0 |
+ | Course 19 | C19 | 0 |
+ | Course 20 | C20 | 0 |
+ | Course 21 | C21 | 0 |
+ | Course 22 | C22 | 0 |
+ | Course 23 | C23 | 0 |
+ | Course 24 | C24 | 0 |
+ | Course 25 | C25 | 0 |
+
+ Scenario: The pagination controls should be hidden if I am not enrolled in any courses
+ When I log in as "student1"
+ Then I should see "No courses" in the "Course overview" "block"
+ And I should not see "Show" in the "Course overview" "block"
+ And ".block_myoverview .dropdown-menu.show" "css_element" should not be visible
+ And ".block_myoverview [data-control='next']" "css_element" should not be visible
+ And ".block_myoverview [data-control='previous']" "css_element" should not be visible
+ And I log out
+
+ Scenario: The pagination controls should be hidden if I am enrolled in 12 courses or less
+ Given the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student1 | C2 | student |
+ | student1 | C3 | student |
+ | student1 | C4 | student |
+ | student1 | C5 | student |
+ | student1 | C6 | student |
+ | student1 | C7 | student |
+ | student1 | C8 | student |
+ | student1 | C9 | student |
+ | student1 | C10 | student |
+ | student1 | C11 | student |
+ | student1 | C12 | student |
+ When I log in as "student1"
+ Then I should not see "Show" in the "Course overview" "block"
+ And ".block_myoverview .dropdown-menu.show" "css_element" should not be visible
+ And ".block_myoverview [data-control='next']" "css_element" should not be visible
+ And ".block_myoverview [data-control='previous']" "css_element" should not be visible
+ And I log out
+
+ Scenario: The default pagination should be 12 courses
+ Given the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student1 | C2 | student |
+ | student1 | C3 | student |
+ | student1 | C4 | student |
+ | student1 | C5 | student |
+ | student1 | C6 | student |
+ | student1 | C7 | student |
+ | student1 | C8 | student |
+ | student1 | C9 | student |
+ | student1 | C10 | student |
+ | student1 | C11 | student |
+ | student1 | C12 | student |
+ | student1 | C13 | student |
+ When I log in as "student1"
+ Then I should see "12" in the "[data-action='limit-toggle']" "css_element"
+ And I log out
+
+ Scenario: I should only see pagination limit options less than total number of enrolled courses
+ Given the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student1 | C2 | student |
+ | student1 | C3 | student |
+ | student1 | C4 | student |
+ | student1 | C5 | student |
+ | student1 | C6 | student |
+ | student1 | C7 | student |
+ | student1 | C8 | student |
+ | student1 | C9 | student |
+ | student1 | C10 | student |
+ | student1 | C11 | student |
+ | student1 | C12 | student |
+ | student1 | C13 | student |
+ And I log in as "student1"
+ When I click on "[data-action='limit-toggle']" "css_element" in the "Course overview" "block"
+ Then I should see "All" in the ".dropdown-menu.show" "css_element"
+ And I should see "12" in the ".dropdown-menu.show" "css_element"
+ And ".block_myoverview [data-control='next']" "css_element" should be visible
+ And ".block_myoverview [data-control='previous']" "css_element" should be visible
+ But I should not see "24" in the ".block_myoverview .dropdown-menu.show" "css_element"
+ And I log out
+
+ Scenario: Previous page button should be disabled when on the first page of courses
+ Given the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student1 | C2 | student |
+ | student1 | C3 | student |
+ | student1 | C4 | student |
+ | student1 | C5 | student |
+ | student1 | C6 | student |
+ | student1 | C7 | student |
+ | student1 | C8 | student |
+ | student1 | C9 | student |
+ | student1 | C10 | student |
+ | student1 | C11 | student |
+ | student1 | C12 | student |
+ | student1 | C13 | student |
+ When I log in as "student1"
+ Then the "class" attribute of ".block_myoverview [data-control='previous']" "css_element" should contain "disabled"
+ And I log out
+
+ Scenario: Next page button should be disabled when on the last page of courses
+ Given the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student1 | C2 | student |
+ | student1 | C3 | student |
+ | student1 | C4 | student |
+ | student1 | C5 | student |
+ | student1 | C6 | student |
+ | student1 | C7 | student |
+ | student1 | C8 | student |
+ | student1 | C9 | student |
+ | student1 | C10 | student |
+ | student1 | C11 | student |
+ | student1 | C12 | student |
+ | student1 | C13 | student |
+ When I log in as "student1"
+ And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
+ And I wait until ".block_myoverview [data-control='next']" "css_element" exists
+ Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should contain "disabled"
+ And I log out
+
+ Scenario: Next and previous page buttons should both be enabled when not on last or first page of courses
+ Given the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | student1 | C2 | student |
+ | student1 | C3 | student |
+ | student1 | C4 | student |
+ | student1 | C5 | student |
+ | student1 | C6 | student |
+ | student1 | C7 | student |
+ | student1 | C8 | student |
+ | student1 | C9 | student |
+ | student1 | C10 | student |
+ | student1 | C11 | student |
+ | student1 | C12 | student |
+ | student1 | C13 | student |
+ | student1 | C14 | student |
+ | student1 | C15 | student |
+ | student1 | C16 | student |
+ | student1 | C17 | student |
+ | student1 | C18 | student |
+ | student1 | C19 | student |
+ | student1 | C20 | student |
+ | student1 | C21 | student |
+ | student1 | C22 | student |
+ | student1 | C23 | student |
+ | student1 | C24 | student |
+ | student1 | C25 | student |
+ When I log in as "student1"
+ And I click on "[data-control='next']" "css_element" in the "Course overview" "block"
+ And I wait until ".block_myoverview [data-control='next']" "css_element" exists
+ Then the "class" attribute of ".block_myoverview [data-control='next']" "css_element" should not contain "disabled"
+ And the "class" attribute of ".block_myoverview [data-control='previous']" "css_element" should not contain "disabled"
+ And I should see "Course 13" in the "Course overview" "block"
+ And I should see "Course 24" in the "Course overview" "block"
+ But I should not see "Course 12" in the "Course overview" "block"
+ And I should not see "Course 25" in the "Course overview" "block"
+ And I log out
<a href="{{url}}"
title={{#quote}}{{{name}}}{{/quote}}
aria-label='{{#str}} ariaeventlistitem, block_timeline, { "name": {{#quote}}{{{name}}}{{/quote}}, "course": {{#quote}}{{{course.fullnamedisplay}}}{{/quote}}, "date": "{{#userdate}} {{timesort}}, {{#str}} strftimedatetime, core_langconfig {{/str}} {{/userdate}}" } {{/str}}'
- ><h6 class="event-name text-truncate mb-0">{{#quote}}{{{name}}}{{/quote}}</h6></a>
+ ><h6 class="event-name text-truncate mb-0">{{{name}}}</h6></a>
{{#course.fullnamedisplay}}
- <small class="text-muted text-truncate mb-0">{{#quote}}{{{course.fullnamedisplay}}}{{/quote}}</small>
+ <small class="text-muted text-truncate mb-0">{{{course.fullnamedisplay}}}</small>
{{/course.fullnamedisplay}}
{{#action.actionable}}
<h6 class="mb-0 pt-2">
$values['islastday'] = false;
$today = $this->related['type']->timestamp_to_date_array($this->related['today']);
- $values['popupname'] = $this->event->get_name();
+ if ($hascourse) {
+ $values['popupname'] = external_format_string($this->event->get_name(), \context_course::instance($course->id), true);
+ } else {
+ $values['popupname'] = external_format_string($this->event->get_name(), \context_system::instance(), true);
+ }
$times = $this->event->get_times();
if ($duration = $times->get_duration()) {
</td>
<td id="buttonscell">
<div id="addcontrols">
- <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().' '.s(get_string('add')); ?>" title="<?php p(get_string('add')); ?>" /><br />
+ <input class="btn btn-secondary" name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow() . ' ' .
+ s(get_string('add')); ?>" title="<?php p(get_string('add')); ?>" /><br />
</div>
<div id="removecontrols">
- <input name="remove" id="remove" type="submit" value="<?php echo s(get_string('remove')).' '.$OUTPUT->rarrow(); ?>" title="<?php p(get_string('remove')); ?>" />
+ <input class="btn btn-secondary" name="remove" id="remove" type="submit"
+ value="<?php echo s(get_string('remove')) . ' ' . $OUTPUT->rarrow(); ?>"
+ title="<?php p(get_string('remove')); ?>" />
</div>
</td>
<td id="potentialcell">
</td>
</tr>
<tr><td colspan="3" id='backcell'>
- <input type="submit" name="cancel" value="<?php p(get_string('backtocohorts', 'cohort')); ?>" />
+ <input class="btn btn-secondary" type="submit" name="cancel" value="<?php p(get_string('backtocohorts', 'cohort')); ?>" />
</td></tr>
</table>
</div></form>
//
// $CFG->maxcoursesincategory = 10000;
//
+// Admin setting encryption
+//
+// $CFG->secretdataroot = '/var/www/my_secret_folder';
+//
+// Location to store encryption keys. By default this is $CFG->dataroot/secret; set this if
+// you want to use a different location for increased security (e.g. if too many people have access
+// to the main dataroot, or if you want to avoid using shared storage). Your web server user needs
+// read access to this location, and write access unless you manually create the keys.
+//
+// $CFG->nokeygeneration = false;
+//
+// If you change this to true then the server will give an error if keys don't exist, instead of
+// automatically generating them. This is only needed if you want to ensure that keys are consistent
+// across a cluster when not using shared storage. If you stop the server generating keys, you will
+// need to manually generate them by running 'php admin/cli/generate_key.php'.
+
//=========================================================================
// 7. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
//=========================================================================
const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);
toggleFocusableChooserOption(firstChooserOption, true);
- initTabsKeyboardNavigation(body);
initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);
return body;
.catch();
};
-/**
- * Initialise the keyboard navigation controls for the tab list items.
- *
- * @method initTabsKeyboardNavigation
- * @param {HTMLElement} body Our modal that we are working with
- */
-const initTabsKeyboardNavigation = (body) => {
- // Set up the tab handlers.
- const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);
- const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);
- const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);
- const activityTabNav = body.querySelector(selectors.regions.activityTabNav);
- const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav);
- const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav];
- tabNavArray.forEach((element) => {
- return element.addEventListener('keydown', (e) => {
- // The first visible navigation tab link.
- const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);
- // The last navigation tab link. It would always be the default activities tab link.
- const lastLink = e.target.parentElement.lastElementChild;
-
- if (e.keyCode === arrowRight) {
- const nextLink = e.target.nextElementSibling;
- if (nextLink === null) {
- e.target.tabIndex = -1;
- firstLink.tabIndex = 0;
- firstLink.focus();
- } else if (nextLink.classList.contains('d-none')) {
- e.target.tabIndex = -1;
- lastLink.tabIndex = 0;
- lastLink.focus();
- } else {
- e.target.tabIndex = -1;
- nextLink.tabIndex = 0;
- nextLink.focus();
- }
- }
- if (e.keyCode === arrowLeft) {
- const previousLink = e.target.previousElementSibling;
- if (previousLink === null) {
- e.target.tabIndex = -1;
- lastLink.tabIndex = 0;
- lastLink.focus();
- } else if (previousLink.classList.contains('d-none')) {
- e.target.tabIndex = -1;
- firstLink.tabIndex = 0;
- firstLink.focus();
- } else {
- e.target.tabIndex = -1;
- previousLink.tabIndex = 0;
- previousLink.focus();
- }
- }
- if (e.keyCode === home) {
- e.target.tabIndex = -1;
- firstLink.tabIndex = 0;
- firstLink.focus();
- }
- if (e.keyCode === end) {
- e.target.tabIndex = -1;
- lastLink.tabIndex = 0;
- lastLink.focus();
- }
- if (e.keyCode === space) {
- e.preventDefault();
- e.target.click();
- }
- });
- });
-};
-
/**
* Initialise the keyboard navigation controls for the chooser options.
*
help: getDataSelector('region', 'help'),
modules: getDataSelector('region', 'modules'),
favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),
- recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),
defaultTabNav: getDataSelector('region', 'default-tab-nav'),
activityTabNav: getDataSelector('region', 'activity-tab-nav'),
- resourceTabNav: getDataSelector('region', 'resources-tab-nav'),
favouriteTab: getDataSelector('region', 'favourites'),
recommendedTab: getDataSelector('region', 'recommended'),
defaultTab: getDataSelector('region', 'default'),
has_capability('moodle/course:changelockedcustomfields', $context));
} else {
$context = $this->get_parent_context();
- return (!$field->get_configdata_property('locked') ||
- guess_if_creator_will_have_course_capability('moodle/course:changelockedcustomfields', $context));
+ if ($context->contextlevel == CONTEXT_SYSTEM) {
+ return (!$field->get_configdata_property('locked') ||
+ has_capability('moodle/course:changelockedcustomfields', $context));
+ } else {
+ return (!$field->get_configdata_property('locked') ||
+ guess_if_creator_will_have_course_capability('moodle/course:changelockedcustomfields', $context));
+ }
}
}
}
}
if ($viewmode === 'default' || $viewmode === 'combined') {
- $class .= ' viewmode-cobmined';
+ $class .= ' viewmode-combined';
} else {
$class .= ' viewmode-'.$viewmode;
}
And I expand all fieldsets
Then "Mathematics" "autocomplete_suggestions" should exist
And I set the following fields to these values:
- | Tags | Algebra |
+ | Tags | Mathematics, Algebra |
And I press "Save and display"
And I am on "Course 2" course homepage
And I navigate to "Edit settings" in current page administration
And I navigate to "Course tags" in current page administration
Then I should see "Mathematics" in the ".form-autocomplete-selection" "css_element"
And I set the following fields to these values:
- | Tags | Algebra |
+ | Tags | Mathematics, Algebra |
And I press "Save changes"
And I am on "Course 2" course homepage
And I navigate to "Course tags" in current page administration
And "Learner" "button" should exist
And I navigate to course participants
And I set the field "type" in the "Filter 1" "fieldset" to "Roles"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Filter 1" "fieldset"
- And I should see "Tutor (Non-editing teacher)" in the ".form-autocomplete-suggestions" "css_element"
+ And I open the autocomplete suggestions list in the "Filter 1" "fieldset"
And I should see "Learner (Student)" in the ".form-autocomplete-suggestions" "css_element"
+ And I press the escape key
+ And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Tutor (Non-editing teacher)"
+
And I click on "Student 1's role assignments" "link"
And I click on ".form-autocomplete-downarrow" "css_element" in the "Student 1" "table_row"
And "Tutor (Non-editing teacher)" "autocomplete_suggestions" should exist
And "Learner" "button" should not exist
And I navigate to course participants
And I set the field "type" in the "Filter 1" "fieldset" to "Roles"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Filter 1" "fieldset"
+ And I open the autocomplete suggestions list in the "Filter 1" "fieldset"
And I should see "Non-editing teacher" in the ".form-autocomplete-suggestions" "css_element"
And I should see "Student" in the ".form-autocomplete-suggestions" "css_element"
// The call to get_all_content_items() should return the same items as for the course,
// given the user in an editing teacher and can add manual lti instances.
- $this->assertEquals(array_column($allcontentitems, 'name'), array_column($coursecontentitems, 'name'));
+ $this->assertContains('lti', array_column($coursecontentitems, 'name'));
+ $this->assertContains('lti', array_column($allcontentitems, 'name'));
// Now removing the cap 'mod/lti:addinstance'. This will restrict those items returned by the course-specific method.
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
'component' => new external_value(PARAM_COMPONENT, 'component'),
'area' => new external_value(PARAM_ALPHANUMEXT, 'area'),
'itemid' => new external_value(PARAM_INT, 'itemid'),
- 'usescategories' => new external_value(PARAM_INT, 'view has categories'),
+ 'usescategories' => new external_value(PARAM_BOOL, 'view has categories'),
'categories' => new external_multiple_structure(
new external_single_structure(
array(
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_cohort/roleid',
- get_string('defaultrole', 'role'), '', $student->id, $options));
+ get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
$options = array(
ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
$options = get_default_enrol_roles(context_system::instance());
$student = get_archetype_roles('student');
$student = reset($student);
- $settings->add(new admin_setting_configselect('enrol_database/defaultrole', get_string('defaultrole', 'enrol_database'), get_string('defaultrole_desc', 'enrol_database'), $student->id, $options));
+ $settings->add(new admin_setting_configselect('enrol_database/defaultrole',
+ get_string('defaultrole', 'enrol_database'),
+ get_string('defaultrole_desc', 'enrol_database'),
+ $student->id ?? null,
+ $options));
}
$settings->add(new admin_setting_configcheckbox('enrol_database/ignorehiddencourses', get_string('ignorehiddencourses', 'enrol_database'), get_string('ignorehiddencourses_desc', 'enrol_database'), 0));
*/
public static function get_enrolled_users_parameters() {
return new external_function_parameters(
- array(
+ [
'courseid' => new external_value(PARAM_INT, 'course id'),
'options' => new external_multiple_structure(
new external_single_structure(
- array(
+ [
'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
'value' => new external_value(PARAM_RAW, 'option value')
- )
+ ]
), 'Option names:
* withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
* groupid (integer) return only users in this group id. If the course has groups enabled and this param
isn\'t defined, returns all the viewable users.
This option requires \'moodle/site:accessallgroups\' on the course context if the
user doesn\'t belong to the group.
- * onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
+ * onlyactive (integer) return only users with active enrolments and matching time restrictions.
+ This option requires \'moodle/course:enrolreview\' on the course context.
+ Please note that this option can\'t
+ be used together with onlysuspended (only one can be active).
+ * onlysuspended (integer) return only suspended users. This option requires
+ \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t
+ be used together with onlyactive (only one can be active).
* userfields (\'string, string, ...\') return only the values of these user fields.
* limitfrom (integer) sql limit from.
* limitnumber (integer) maximum number of returned users.
* sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
* sortdirection (string) ASC or DESC',
- VALUE_DEFAULT, array()),
- )
+ VALUE_DEFAULT, []),
+ ]
);
}
* }
* @return array An array of users
*/
- public static function get_enrolled_users($courseid, $options = array()) {
+ public static function get_enrolled_users($courseid, $options = []) {
global $CFG, $USER, $DB;
require_once($CFG->dirroot . '/course/lib.php');
$params = self::validate_parameters(
self::get_enrolled_users_parameters(),
- array(
+ [
'courseid'=>$courseid,
'options'=>$options
- )
+ ]
);
$withcapability = '';
$groupid = 0;
$onlyactive = false;
- $userfields = array();
+ $onlysuspended = false;
+ $userfields = [];
$limitfrom = 0;
$limitnumber = 0;
$sortby = 'us.id';
- $sortparams = array();
+ $sortparams = [];
$sortdirection = 'ASC';
foreach ($options as $option) {
switch ($option['name']) {
- case 'withcapability':
- $withcapability = $option['value'];
- break;
- case 'groupid':
- $groupid = (int)$option['value'];
- break;
- case 'onlyactive':
- $onlyactive = !empty($option['value']);
- break;
- case 'userfields':
- $thefields = explode(',', $option['value']);
- foreach ($thefields as $f) {
- $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
- }
- break;
- case 'limitfrom' :
- $limitfrom = clean_param($option['value'], PARAM_INT);
- break;
- case 'limitnumber' :
- $limitnumber = clean_param($option['value'], PARAM_INT);
- break;
- case 'sortby':
- $sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
- if (!in_array($option['value'], $sortallowedvalues)) {
- throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
- 'allowed values are: ' . implode(',', $sortallowedvalues));
- }
- if ($option['value'] == 'siteorder') {
- list($sortby, $sortparams) = users_order_by_sql('us');
- } else {
- $sortby = 'us.' . $option['value'];
- }
- break;
- case 'sortdirection':
- $sortdirection = strtoupper($option['value']);
- $directionallowedvalues = array('ASC', 'DESC');
- if (!in_array($sortdirection, $directionallowedvalues)) {
- throw new invalid_parameter_exception('Invalid value for sortdirection parameter
+ case 'withcapability':
+ $withcapability = $option['value'];
+ break;
+ case 'groupid':
+ $groupid = (int)$option['value'];
+ break;
+ case 'onlyactive':
+ $onlyactive = !empty($option['value']);
+ break;
+ case 'onlysuspended':
+ $onlysuspended = !empty($option['value']);
+ break;
+ case 'userfields':
+ $thefields = explode(',', $option['value']);
+ foreach ($thefields as $f) {
+ $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
+ }
+ break;
+ case 'limitfrom' :
+ $limitfrom = clean_param($option['value'], PARAM_INT);
+ break;
+ case 'limitnumber' :
+ $limitnumber = clean_param($option['value'], PARAM_INT);
+ break;
+ case 'sortby':
+ $sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder'];
+ if (!in_array($option['value'], $sortallowedvalues)) {
+ throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' .
+ $option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues));
+ }
+ if ($option['value'] == 'siteorder') {
+ list($sortby, $sortparams) = users_order_by_sql('us');
+ } else {
+ $sortby = 'us.' . $option['value'];
+ }
+ break;
+ case 'sortdirection':
+ $sortdirection = strtoupper($option['value']);
+ $directionallowedvalues = ['ASC', 'DESC'];
+ if (!in_array($sortdirection, $directionallowedvalues)) {
+ throw new invalid_parameter_exception('Invalid value for sortdirection parameter
(value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
- }
- break;
+ }
+ break;
}
}
- $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+ $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
$coursecontext = context_course::instance($courseid, IGNORE_MISSING);
if ($courseid == SITEID) {
$context = context_system::instance();
require_capability('moodle/site:accessallgroups', $coursecontext);
}
// to overwrite this option, you need course:enrolereview permission
- if ($onlyactive) {
+ if ($onlyactive || $onlysuspended) {
require_capability('moodle/course:enrolreview', $coursecontext);
}
- list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
+ list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
+ $onlysuspended);
$ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
$ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
$enrolledparams['contextlevel'] = CONTEXT_USER;
$enrolledparams = array_merge($enrolledparams, $groupparams);
} else {
// User doesn't belong to any group, so he can't see any user. Return an empty array.
- return array();
+ return [];
}
}
$sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
$enrolledparams['courseid'] = $courseid;
$enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
- $users = array();
+ $users = [];
foreach ($enrolledusers as $user) {
context_helper::preload_from_record($user);
if ($userdetails = user_get_user_details($user, $course, $userfields)) {
public static function get_enrolled_users_returns() {
return new external_multiple_structure(
new external_single_structure(
- array(
+ [
'id' => new external_value(PARAM_INT, 'ID of the user'),
'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
'customfields' => new external_multiple_structure(
new external_single_structure(
- array(
+ [
'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
- )
+ ]
), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
'groups' => new external_multiple_structure(
new external_single_structure(
- array(
+ [
'id' => new external_value(PARAM_INT, 'group id'),
'name' => new external_value(PARAM_RAW, 'group name'),
'description' => new external_value(PARAM_RAW, 'group description'),
'descriptionformat' => new external_format_value('description'),
- )
+ ]
), 'user groups', VALUE_OPTIONAL),
'roles' => new external_multiple_structure(
new external_single_structure(
- array(
+ [
'roleid' => new external_value(PARAM_INT, 'role id'),
'name' => new external_value(PARAM_RAW, 'role name'),
'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
'sortorder' => new external_value(PARAM_INT, 'role sortorder')
- )
+ ]
), 'user roles', VALUE_OPTIONAL),
'preferences' => new external_multiple_structure(
new external_single_structure(
- array(
+ [
'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
- )
+ ]
), 'User preferences', VALUE_OPTIONAL),
'enrolledcourses' => new external_multiple_structure(
new external_single_structure(
- array(
+ [
'id' => new external_value(PARAM_INT, 'Id of the course'),
'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
- )
+ ]
), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
- )
+ ]
)
);
}
$currencies[$c] = new lang_string($c, 'core_currencies');
}
+ uasort($currencies, function($a, $b) {
+ return strcmp($a, $b);
+ });
+
return $currencies;
}
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_fee/roleid',
- get_string('defaultrole', 'enrol_fee'), get_string('defaultrole_desc', 'enrol_fee'), $student->id, $options));
+ get_string('defaultrole', 'enrol_fee'), get_string('defaultrole_desc', 'enrol_fee'), $student->id ?? null, $options));
}
$settings->add(new admin_setting_configduration('enrol_fee/enrolperiod',
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_manual/roleid',
- get_string('defaultrole', 'role'), '', $student->id, $options));
+ get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
}
$options = array(2 => get_string('coursestart'), 3 => get_string('today'), 4 => get_string('now', 'enrol_manual'));
Scenario: Searching for a non-existing user
Given I navigate to course participants
And I press "Enrol users"
- And I set the field "Select users" to "qwertyuiop"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
+ And I click on "Select users" "field"
+ And I type "qwertyuiop"
Then I should see "No suggestions"
@javascript
Scenario: If there are less than 100 matching users, all are displayed for selection
Given I navigate to course participants
And I press "Enrol users"
- When I set the field "Select users" to "example.com"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
- And I click on "Student 099" item in the autocomplete list
- Then I should see "Student 099"
+ When I click on "Select users" "field"
+ And I type "example.com"
+ Then "Student 099" "autocomplete_suggestions" should exist
@javascript
Scenario: If there are more than 100 matching users, inform there are too many.
| student101 | Student | 101 | student101@example.com |
And I navigate to course participants
And I press "Enrol users"
- When I set the field "Select users" to "example.com"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
+ When I click on "Select users" "field"
+ And I type "example.com"
Then I should see "Too many users (>100) to show"
@javascript
| maxusersperpage | 5 |
And I navigate to course participants
And I press "Enrol users"
- When I set the field "Select users" to "student00"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
+ When I click on "Select users" "field"
+ And I type "student00"
Then I should see "Too many users (>5) to show"
@javascript
When I am on "Course 001" course homepage
Then I navigate to course participants
And I press "Enrol users"
- When I set the field "Select users" to "student100@example.com"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
+ And I click on "Select users" "field"
+ And I type "student100@example.com"
Then I should see "student100@example.com, CITY1, GB, 1234567892, 1234567893, ABC1, ABC2"
# Remove identity field in setting User policies
And the following config values are set as admin:
| showuseridentity | idnumber,email,phone1,phone2,department,institution |
- When I am on "Course 001" course homepage
+ And I am on "Course 001" course homepage
And I navigate to course participants
And I press "Enrol users"
- When I set the field "Select users" to "student100@example.com"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Select users" "form_row"
- Then I should see "student100@example.com, 1234567892, 1234567893, ABC1, ABC2"
+ And I click on "Select users" "field"
+ And I type "student100@example.com"
+ And I should see "student100@example.com, 1234567892, 1234567893, ABC1, ABC2"
# The following tests are commented out as a result of MDL-66339.
# @javascript
$student = reset($student);
$settings->add(new admin_setting_configselect_with_advanced('enrol_mnet/roleid',
get_string('defaultrole', 'role'), '',
- array('value'=>$student->id, 'adv'=>true), $options));
+ array('value' => $student->id ?? null, 'adv' => true), $options));
}
}
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_paypal/roleid',
- get_string('defaultrole', 'enrol_paypal'), get_string('defaultrole_desc', 'enrol_paypal'), $student->id, $options));
+ get_string('defaultrole', 'enrol_paypal'),
+ get_string('defaultrole_desc', 'enrol_paypal'),
+ $student->id ?? null,
+ $options));
}
$settings->add(new admin_setting_configduration('enrol_paypal/enrolperiod',
$student = get_archetype_roles('student');
$student = reset($student);
$settings->add(new admin_setting_configselect('enrol_self/roleid',
- get_string('defaultrole', 'enrol_self'), get_string('defaultrole_desc', 'enrol_self'), $student->id, $options));
+ get_string('defaultrole', 'enrol_self'),
+ get_string('defaultrole_desc', 'enrol_self'),
+ $student->id ?? null,
+ $options));
}
$settings->add(new admin_setting_configduration('enrol_self/enrolperiod',
}
}
+ /**
+ * Verify get_enrolled_users() returned users according to their status.
+ */
+ public function test_get_enrolled_users_active_suspended() {
+ global $USER;
+
+ $this->resetAfterTest();
+
+ // Create the course and the users.
+ $course = $this->getDataGenerator()->create_course();
+ $coursecontext = context_course::instance($course->id);
+ $user0 = $this->getDataGenerator()->create_user(['username' => 'user0active']);
+ $user1 = $this->getDataGenerator()->create_user(['username' => 'user1active']);
+ $user2 = $this->getDataGenerator()->create_user(['username' => 'user2active']);
+ $user2su = $this->getDataGenerator()->create_user(['username' => 'user2suspended']); // Suspended user.
+ $user3 = $this->getDataGenerator()->create_user(['username' => 'user3active']);
+ $user3su = $this->getDataGenerator()->create_user(['username' => 'user3suspended']); // Suspended user.
+
+ // Enrol the users in the course.
+ $this->getDataGenerator()->enrol_user($user0->id, $course->id);
+ $this->getDataGenerator()->enrol_user($user1->id, $course->id);
+ $this->getDataGenerator()->enrol_user($user2->id, $course->id);
+ $this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
+ $this->getDataGenerator()->enrol_user($user3->id, $course->id);
+ $this->getDataGenerator()->enrol_user($user3su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
+
+ // Create a role to add the allowedcaps. Users will have this role assigned.
+ $roleid = $this->getDataGenerator()->create_role();
+ // Allow the specified capabilities.
+ assign_capability('moodle/course:enrolreview', CAP_ALLOW, $roleid, $coursecontext);
+ assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $roleid, $coursecontext);
+
+ // Switch to the user and assign the role.
+ $this->setUser($user0);
+ role_assign($roleid, $USER->id, $coursecontext);
+
+ // Suspended users.
+ $options = [
+ ['name' => 'onlysuspended', 'value' => true],
+ ['name' => 'userfields', 'value' => 'id,username']
+ ];
+ $suspendedusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $suspendedusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $suspendedusers);
+ $this->assertCount(2, $suspendedusers);
+
+ foreach ($suspendedusers as $suspendeduser) {
+ $this->assertStringContainsString('suspended', $suspendeduser['username']);
+ }
+
+ // Active users.
+ $options = [
+ ['name' => 'onlyactive', 'value' => true],
+ ['name' => 'userfields', 'value' => 'id,username']
+ ];
+ $activeusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $activeusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $activeusers);
+ $this->assertCount(4, $activeusers);
+
+ foreach ($activeusers as $activeuser) {
+ $this->assertStringContainsString('active', $activeuser['username']);
+ }
+
+ // All enrolled users.
+ $options = [
+ ['name' => 'userfields', 'value' => 'id,username']
+ ];
+ $allusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $allusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $allusers);
+ $this->assertCount(6, $allusers);
+
+ // Active and suspended. Test exception is thrown.
+ $options = [
+ ['name' => 'onlyactive', 'value' => true],
+ ['name' => 'onlysuspended', 'value' => true],
+ ['name' => 'userfields', 'value' => 'id,username']
+ ];
+ $this->expectException('coding_exception');
+ $message = 'Coding error detected, it must be fixed by a programmer: Both onlyactive ' .
+ 'and onlysuspended are set, this is probably not what you want!';
+ $this->expectExceptionMessage($message);
+ core_enrol_external::get_enrolled_users($course->id, $options);
+ }
+
/**
* Test get_users_courses
*/
This files describes API changes in /enrol/* - plugins,
information provided here is intended especially for developers.
+=== 3.11 ===
+
+* Added onlysuspended option to core_enrol_get_enrolled_users webservice to retrieve only suspended users.
+
=== 3.8 ===
* Function enrol_manual_plugin::enrol_cohort now return the number of enrolled cohort users.
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Abstraction of general file archives.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_files;
+
+use core_files\local\archive_writer\file_writer_interface as file_writer_interface;
+use core_files\local\archive_writer\stream_writer_interface as stream_writer_interface;
+
+/**
+ * Each file archive type must extend this class.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class archive_writer {
+
+ /**
+ * The zip writer class.
+ */
+ public const ZIP_WRITER = 'zip_writer';
+
+ /**
+ * Returns the stream writer.
+ *
+ * @param string $filename
+ * @param string $type
+ * @return stream_writer_interface
+ */
+ public static function get_stream_writer(string $filename, string $type): stream_writer_interface {
+ $classname = self::get_classname_for_type($type);
+
+ if (!is_a($classname, stream_writer_interface::class, true)) {
+ throw new \InvalidArgumentException("{$type} does not support streaming");
+ }
+
+ return $classname::stream_instance($filename);
+ }
+
+ /**
+ * Returns the file writer.
+ *
+ * @param string $filepath
+ * @param string $type
+ * @return file_writer_interface
+ */
+ public static function get_file_writer(string $filepath, string $type): file_writer_interface {
+ $classname = self::get_classname_for_type($type);
+
+ if (!is_a($classname, file_writer_interface::class, true)) {
+ throw new \InvalidArgumentException("{$type} does not support writing to files");
+ }
+
+ return $classname::file_instance($filepath);
+ }
+
+ /**
+ * Sanitise the file path, removing any unsuitable characters.
+ *
+ * @param string $filepath
+ * @return string
+ */
+ public function sanitise_filepath(string $filepath): string {
+ return clean_param($filepath, PARAM_PATH);
+ }
+
+ /**
+ * Returns the class name for the type that was provided in get_file_writer().
+ *
+ * @param string $type
+ * @return string
+ */
+ protected static function get_classname_for_type(string $type): string {
+ return "core_files\local\archive_writer\\" . $type;
+ }
+
+ /**
+ * The archive_writer Constructor.
+ */
+ protected function __construct() {
+
+ }
+
+ /**
+ * Adds a file from a file path.
+ *
+ * @param string $name The path of file in archive (including directory).
+ * @param string $path The path to file on disk (note: paths should be encoded using
+ * UNIX-style forward slashes -- e.g '/path/to/some/file').
+ */
+ abstract public function add_file_from_filepath(string $name, string $path): void;
+
+ /**
+ * Adds a file from a string.
+ *
+ * @param string $name The path of file in archive (including directory).
+ * @param string $data The contents of file
+ */
+ abstract public function add_file_from_string(string $name, string $data): void;
+
+ /**
+ * Adds a file from a stream.
+ *
+ * @param string $name The path of file in archive (including directory).
+ * @param resource $stream The contents of file as a stream resource
+ */
+ abstract public function add_file_from_stream(string $name, $stream): void;
+
+ /**
+ * Adds a stored_file to archive.
+ *
+ * @param string $name The path of file in archive (including directory).
+ * @param \stored_file $file
+ */
+ abstract public function add_file_from_stored_file(string $name, \stored_file $file): void;
+
+ /**
+ * Finish writing the zip footer.
+ */
+ abstract public function finish(): void;
+}
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Interface used by archives that write to files.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_files\local\archive_writer;
+
+/**
+ * Interface used by archives that write to files.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface file_writer_interface {
+
+ /**
+ * Return the file instance.
+ *
+ * @param string $filename
+ * @return static
+ */
+ public static function file_instance(string $filename): self;
+
+ /**
+ * Get the path of the zip.
+ *
+ * @return string
+ */
+ public function get_path_to_zip(): string;
+}
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Interface used by archives that write to streams.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_files\local\archive_writer;
+
+/**
+ * Interface used by archives that write to streams.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+interface stream_writer_interface {
+
+ /**
+ * Return the stream instance.
+ *
+ * @param string $filename
+ * @return static
+ */
+ public static function stream_instance(string $filename): self;
+}
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Class used for creating ZIP archives.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace core_files\local\archive_writer;
+
+use ZipStream\Option\Archive;
+use ZipStream\ZipStream;
+use core_files\archive_writer;
+use core_files\local\archive_writer\file_writer_interface as file_writer_interface;
+use core_files\local\archive_writer\stream_writer_interface as stream_writer_interface;
+
+/**
+ * Class used for creating ZIP archives.
+ *
+ * @package core_files
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class zip_writer extends archive_writer implements file_writer_interface, stream_writer_interface {
+
+ /**
+ * @var resource File resource for the file handle for a file-based zip stream
+ */
+ private $zipfilehandle = null;
+
+ /**
+ * @var String The location of the zip file.
+ */
+ private $zipfilepath = null;
+
+ /**
+ * @var ZipStream The zip stream.
+ */
+ private $archive;
+
+ /**
+ * The zip_writer constructor.
+ *
+ * @param ZipStream $archive
+ */
+ protected function __construct(ZipStream $archive) {
+ parent::__construct();
+ $this->archive = $archive;
+ }
+
+ public static function stream_instance(string $filename): stream_writer_interface {
+ $options = new Archive();
+ $options->setSendHttpHeaders(true);
+ $options->setContentDisposition('attachment');
+ $options->setContentType('application/x-zip');
+ $zipwriter = new ZipStream($filename, $options);
+
+ return new static($zipwriter);
+ }
+
+ public static function file_instance(string $filename): file_writer_interface {
+ $dir = make_request_directory();
+ $filepath = "$dir/$filename";
+ $fh = fopen($filepath, 'w');
+
+ $exportoptions = new Archive();
+ $exportoptions->setOutputStream($fh);
+ $exportoptions->setSendHttpHeaders(false);
+ $zipstream = new ZipStream($filename, $exportoptions);
+
+ $zipwriter = new static($zipstream);
+ // ZipStream only takes a file handle resource.
+ // It does not close this resource itself, and it does not know the location of this resource on disk.
+ // Store references to the filehandle, and the location of the filepath in the new class so that the `finish()`
+ // function can close the fh, and move the temporary file into place.
+ // The filehandle must be closed when finishing the archive. ZipStream does not close it automatically.
+ $zipwriter->zipfilehandle = $fh;
+ $zipwriter->zipfilepath = $filepath;
+
+ return $zipwriter;
+ }
+
+ public function add_file_from_filepath(string $name, string $path): void {
+ $this->archive->addFileFromPath($this->sanitise_filepath($name), $path);
+ }
+
+ public function add_file_from_string(string $name, string $data): void {
+ $this->archive->addFile($this->sanitise_filepath($name), $data);
+ }
+
+ public function add_file_from_stream(string $name, $stream): void {
+ $this->archive->addFileFromStream($this->sanitise_filepath($name), $stream);
+ fclose($stream);
+ }
+
+ public function add_file_from_stored_file(string $name, \stored_file $file): void {
+ $filehandle = $file->get_content_file_handle();
+ $this->archive->addFileFromStream($this->sanitise_filepath($name), $filehandle);
+ fclose($filehandle);
+ }
+
+ public function finish(): void {
+ $this->archive->finish();
+
+ if ($this->zipfilehandle) {
+ fclose($this->zipfilehandle);
+ }
+ }
+
+ public function get_path_to_zip(): string {
+ return $this->zipfilepath;
+ }
+
+ public function sanitise_filepath(string $filepath): string {
+ $filepath = parent::sanitise_filepath($filepath);
+
+ return \ZipStream\File::filterFilename($filepath);
+ }
+}
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Unit tests for core_files\local\archive_writer/zip_archive.
+ *
+ * @package core_files
+ * @category test
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ */
+
+namespace core_files;
+
+use advanced_testcase;
+use core_files\local\archive_writer\zip_writer;
+
+/**
+ * Unit tests for \core_files\archive_writer.
+ *
+ * @coversDefaultClass \core_files\archive_writer
+ */
+class archive_writer_testcase extends advanced_testcase {
+
+ /**
+ * Test get_file_writer().
+ */
+ public function test_get_file_writer(): void {
+ $zipwriter = archive_writer::get_file_writer('file.zip', archive_writer::ZIP_WRITER);
+ $this->assertInstanceOf(zip_writer::class, $zipwriter);
+ $this->assertTrue(file_exists($zipwriter->get_path_to_zip()));
+ }
+
+ /**
+ * Test get_stream_writer().
+ */
+ public function test_get_stream_writer(): void {
+ $zipwriter = archive_writer::get_stream_writer('path/to/file.txt', archive_writer::ZIP_WRITER);
+ $this->assertInstanceOf(zip_writer::class, $zipwriter);
+ }
+}
--- /dev/null
+Hey, this is an awesome text file. Hello! :)
\ No newline at end of file
--- /dev/null
+<?php
+// 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/>.
+
+/**
+ * Unit tests for \core_files\local\archive_writer\zip_writer.
+ *
+ * @package core_files
+ * @category test
+ * @copyright 2020 Mark Nelson <mdjnelson@gmail.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
+ */
+
+namespace core_files\local\archive_writer;
+
+use advanced_testcase;
+use context_module;
+use core_files\archive_writer;
+use ZipArchive;
+
+/**
+ * Unit tests for \core_files\local\archive_writer\zip_writer.
+ *
+ * @coversDefaultClass \core_files\local\archive_writer\zip_writer
+ */
+class zip_writer_testcase extends advanced_testcase {
+
+ /**
+ * Test add_file_from_filepath().
+ */
+ public function test_add_file_from_filepath(): void {
+ global $CFG;
+
+ $pathtofileinzip = '/some/made/up/name.txt';
+ $filetoadd = $CFG->dirroot . '/files/tests/fixtures/awesome_file.txt';
+
+ $zipwriter = archive_writer::get_file_writer('test.zip', archive_writer::ZIP_WRITER);
+ $zipwriter->add_file_from_filepath($pathtofileinzip, $filetoadd);
+ $zipwriter->finish();
+
+ $pathtozip = $zipwriter->get_path_to_zip();
+ $zip = new ZipArchive();
+ $opened = $zip->open($pathtozip);
+ $this->assertTrue($opened);
+
+ $pathtofileinzip = $zipwriter->sanitise_filepath($pathtofileinzip);
+
+ $this->assertEquals("Hey, this is an awesome text file. Hello! :)", $zip->getFromName($pathtofileinzip));
+ }
+
+ /**
+ * Test add_file_from_string().
+ */
+ public function test_add_file_from_string(): void {
+ $pathtofileinzip = "/path/to/my/awesome/file.txt";
+ $mycontent = "This is some real awesome content, ya dig?";
+
+ $zipwriter = archive_writer::get_file_writer('test.zip', archive_writer::ZIP_WRITER);
+ $zipwriter->add_file_from_string($pathtofileinzip, $mycontent);
+ $zipwriter->finish();
+
+ $pathtozip = $zipwriter->get_path_to_zip();
+ $zip = new ZipArchive();
+ $opened = $zip->open($pathtozip);
+ $this->assertTrue($opened);
+
+ $pathtofileinzip = $zipwriter->sanitise_filepath($pathtofileinzip);
+
+ $this->assertEquals($mycontent, $zip->getFromName($pathtofileinzip));
+ }
+
+ /**
+ * Test add_file_from_stream().
+ */
+ public function test_add_file_from_stream(): void {
+ $this->resetAfterTest(true);
+ $this->setAdminUser();
+
+ $course = $this->getDataGenerator()->create_course();
+ $assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
+
+ // Add a file to the intro.
+ $filerecord = [
+ 'contextid' => context_module::instance($assign->cmid)->id,
+ 'component' => 'mod_assign',
+ 'filearea' => 'intro',
+ 'itemid' => 0,
+ 'filepath' => '/',
+ 'filename' => 'fileintro.txt',
+ ];
+ $fs = get_file_storage();
+ $storedfile = $fs->create_file_from_string($filerecord, 'Contents for the assignment, yeow!');
+
+ $pathtofileinzip = $storedfile->get_filepath() . $storedfile->get_filename();
+
+ $zipwriter = archive_writer::get_file_writer('test.zip', archive_writer::ZIP_WRITER);
+ $zipwriter->add_file_from_stream($pathtofileinzip, $storedfile->get_content_file_handle());
+ $zipwriter->finish();
+
+ $pathtozip = $zipwriter->get_path_to_zip();
+ $zip = new ZipArchive();
+ $opened = $zip->open($pathtozip);
+ $this->assertTrue($opened);
+
+ $pathtofileinzip = $zipwriter->sanitise_filepath($pathtofileinzip);
+
+ $this->assertEquals($storedfile->get_content(), $zip->getFromName($pathtofileinzip));
+ }
+
+ /**
+ * Test add_file_from_stored_file().
+ */
+ public function test_add_file_from_stored_file(): void {
+ $this->resetAfterTest(true);
+ $this->setAdminUser();
+
+ $course = $this->getDataGenerator()->create_course();
+ $assign = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);
+
+ // Add a file to the intro.
+ $filerecord = [
+ 'contextid' => context_module::instance($assign->cmid)->id,
+ 'component' => 'mod_assign',
+ 'filearea' => 'intro',
+ 'itemid' => 0,
+ 'filepath' => '/',
+ 'filename' => 'fileintro.txt',
+ ];
+ $fs = get_file_storage();
+ $storedfile = $fs->create_file_from_string($filerecord, 'Contents for the assignment, yeow!');
+
+ $pathtofileinzip = $storedfile->get_filepath() . $storedfile->get_filename();
+
+ $zipwriter = archive_writer::get_file_writer('test.zip', archive_writer::ZIP_WRITER);
+ $zipwriter->add_file_from_stored_file($pathtofileinzip, $storedfile);
+ $zipwriter->finish();
+
+ $pathtozip = $zipwriter->get_path_to_zip();
+ $zip = new ZipArchive();
+ $opened = $zip->open($pathtozip);
+ $this->assertTrue($opened);
+
+ $pathtofileinzip = $zipwriter->sanitise_filepath($pathtofileinzip);
+
+ $this->assertEquals($storedfile->get_content(), $zip->getFromName($pathtofileinzip));
+ }
+
+ /**
+ * Test sanitise_filepath().
+ *
+ * @param string $providedfilepath The provided file path.
+ * @param string $expectedfilepath The expected file path.
+ * @dataProvider sanitise_filepath_provider
+ */
+ public function test_sanitise_filepath(string $providedfilepath, string $expectedfilepath): void {
+ $zipwriter = archive_writer::get_stream_writer('path/to/file.txt', archive_writer::ZIP_WRITER);
+ $this->assertEquals($expectedfilepath, $zipwriter->sanitise_filepath($providedfilepath));
+ }
+
+ /**
+ * Data provider for test_sanitise_filepath.
+ *
+ * @return array
+ */
+ public function sanitise_filepath_provider(): array {
+ return [
+ ['a../../file/path', 'a../file/path'],
+ ['a./file/path', 'a./file/path'],
+ ['../file/path', 'file/path'],
+ ['foo/bar/', 'foo/bar/'],
+ ['\\\\\\a\\\\\\file\\\\\\path', 'a/file/path'],
+ ['//a//file/////path////', 'a/file/path/']
+ ];
+ }
+}
}
$returnvalue['maxscore'] = $maxscore;
$returnvalue['minscore'] = 0;
- if (!empty($this->moduleinstance->grade)) {
- $graderange = make_grades_menu($this->moduleinstance->grade);
+ $fieldname = \core_grades\component_gradeitems::get_field_name_for_itemname($this->component, $this->area, 'grade');
+ if (!empty($this->moduleinstance->{$fieldname})) {
+ $graderange = make_grades_menu($this->moduleinstance->{$fieldname});
$returnvalue['modulegrade'] = count($graderange) - 1;
}
return $returnvalue;
}
.gradingform_rubric .criterion .description {
- width: 150px;
+ min-width: 150px;
font-weight: bold;
}
| Ratings > Grade to pass | 90 |
And I navigate to "View > Grader report" in the course gradebook
And I turn editing mode on
- And I click on "Edit forum Rating grade for Test Forum 1" "link"
+ And I click on "Edit forum Test Forum 1 rating" "link"
And I expand all fieldsets
Then the field "Grade to pass" matches value "90"
And I set the field "Grade to pass" to "80"
And the "members" select box should not contain "Student 1 (student1@example.com)"
And I navigate to course participants
And I set the field "type" in the "Filter 1" "fieldset" to "Groups"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Filter 1" "fieldset"
- And I click on "Group 1" "list_item"
+ And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group 1"
And I click on "Apply filters" "button"
And I should see "Student 0"
And I should see "Student 1"
And I should not see "Student 2"
- And I click on "Group 1" "autocomplete_selection"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Filter 1" "fieldset"
- And I click on "Group 2" "list_item"
+ And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group 2"
And I click on "Apply filters" "button"
And I should see "Student 2"
And I should see "Student 3"
And I should see "Description for Group A"
And ".groupinfobox" "css_element" should exist
And I set the field "type" in the "Filter 1" "fieldset" to "Groups"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Filter 1" "fieldset"
- And I click on "Group B" "list_item"
+ And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group B"
And I click on "Apply filters" "button"
And I click on "Student 2" "link" in the "participants" "table"
And I click on "Group B" "link"
And I should see "Description for Group A"
And ".groupinfobox" "css_element" should exist
And I set the field "type" in the "Filter 1" "fieldset" to "Groups"
- And I click on ".form-autocomplete-downarrow" "css_element" in the "Filter 1" "fieldset"
- And I click on "Group B" "list_item"
+ And I set the field "Type or select..." in the "Filter 1" "fieldset" to "Group B"
And I click on "Apply filters" "button"
And I click on "Student 2" "link" in the "participants" "table"
And I click on "Group B" "link"
$url,
$config,
$this->factory,
- $this->messages
+ $this->messages,
+ $this->preventredirect
);
if ($file) {
$this->context = \context::instance_by_id($file->get_contextid());
--- /dev/null
+<?php
+// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['thislanguage'] = 'Аԥсуа бызшәа';
defined('MOODLE_INTERNAL') || die();
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'rtl';
$string['thislanguage'] = 'العربية';
--- /dev/null
+<?php
+// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['language'] = 'اللغة';
+$string['next'] = 'التالي';
+$string['previous'] = 'السابق';
+$string['reload'] = 'إعادة تحميل';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'বাংলা';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Català';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Čeština';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Dansk';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Deutsch';
$string['welcomep20'] = 'Βλέπετε αυτή τη σελίδα γιατί εγκαταστήσατε και ξεκινήσατε με επιτυχία το πακέτο <strong>{$a->packname} {$a->packversion}</strong> στον υπολογιστή σας. Συγχαρητήρια!';
$string['welcomep30'] = 'Αυτή η έκδοση/διανομή <strong>{$a->installername}</strong> περιλαμβάνει τις εφαρμογές για τη δημιουργία ενός περιβάλλοντος μέσα στο οποίο θα λειτουργεί το <strong>Moodle</strong>, ονομαστικά:';
$string['welcomep40'] = 'Το πακέτο περιλαμβάνει επίσης το <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
-$string['welcomep50'] = 'Η χρήση όλων των εφαρμογών σε αυτό το πακέτο υπόκειται στις αντίστοιχες άδειες. Ολόκληρο το πακέτο <strong>{$a->installername}</strong> είναι <a href="https://www.opensource.org/docs/definition_plain.html">λογισμικό ανοικτού κώδικα</a> και διανέμεται με την <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a> άδεια.';
+$string['welcomep50'] = 'Η χρήση όλων των εφαρμογών σε αυτό το πακέτο υπόκειται στις αντίστοιχες άδειες χρήσης. Ολόκληρο το πακέτο <strong>{$a->installername}</strong> είναι <a href="https://www.opensource.org/docs/definition_plain.html">λογισμικό ανοικτού κώδικα</a> και διανέμεται με την άδεια χρήσης <a href="https://www.gnu.org/copyleft/gpl.html">GPL</a>.';
$string['welcomep60'] = 'Οι παρακάτω σελίδες θα σας καθοδηγήσουν με εύκολα βήματα στην εγκατάσταση και ρύθμιση του <strong>Moodle</strong> στον υπολογιστή σας. Μπορείτε να δεχθείτε τις προεπιλεγμένες ρυθμίσεις ή προαιρετικά, να τις τροποποιήσετε ανάλογα με τις ανάγκες σας.';
$string['welcomep70'] = 'Πατήστε το κουμπί «Συνέχεια» για να συνεχίσετε με την εγκατάσταση του <strong>Moodle</strong>.';
$string['wwwroot'] = 'Διεύθυνση ιστού';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Ελληνικά';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'English';
defined('MOODLE_INTERNAL') || die();
+$string['parentlanguage'] = 'en_us';
$string['thisdirection'] = 'ltr';
-$string['thislanguage'] = 'American English K12';
+$string['thislanguage'] = 'English (United States) K12';
defined('MOODLE_INTERNAL') || die();
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Español - Internacional';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Español (México)';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'eesti';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Euskara';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Français';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Gaeilge';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Galego';
defined('MOODLE_INTERNAL') || die();
+$string['parentlanguage'] = 'he';
$string['thisdirection'] = 'rtl';
$string['thislanguage'] = 'עברית';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'magyar';
$string['cliansweryes'] = 'այո';
$string['cliincorrectvalueerror'] = 'Սխալ, "{$a->value}"-ը ոչ ճիշտ արժեք է "{$a->option}"-ի համար';
$string['cliincorrectvalueretry'] = 'Ոչ ճիշտ արժեք, կրկին փորձեք';
+$string['clitypevalue'] = 'մուտքագրեք արժեք';
+$string['clitypevaluedefault'] = 'մուտքագրեք արժեք, սեղմեք Enter ՝ կանխադրված արժեքն օգտագործելու համար';
+$string['cliunknowoption'] = 'Չճանաչված տարբերակներ՝
+ {$a}
+Խնդրում ենք օգտագործել - help տարբերակը:';
$string['cliyesnoprompt'] = 'Մուտքագրեք y (նշանակում է այո) կամ n (նշանակում է ոչ):';
$string['environmentrequireinstall'] = 'անհրաժեշտ է, որպեսզի տեղակայված և թույլատրված լինի';
$string['environmentrequireversion'] = 'պահանջվում է {$a->needed} տարբերակը, դուք գործարկում եք՝ {$a->current}';
{$a}
Silahkan gunakan opsi --help';
$string['cliyesnoprompt'] = 'ketik y (Ya) atau t (Tidak)';
+$string['environmentrequireinstall'] = 'harus dipasang dan diaktifkan';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Italiano';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = '日本語';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = '한국어';
--- /dev/null
+<?php
+// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['clianswerno'] = 'Жок';
+$string['cliansweryes'] = 'Ооба';
+$string['cliincorrectvalueerror'] = 'Ката, «{$a->option}» үчүн «{$a->value}» туура эмес маани';
+$string['cliincorrectvalueretry'] = 'Туура эмес маани, кайрадан аракет кылып көрүңүз';
+$string['clitypevalue'] = 'Маанисин киргизгиле';
+$string['clitypevaluedefault'] = 'Маанисин киргизгиле, алгач белгиленген ({$a}) маанисин колдонуу үчүн Enter баскычын баскыла';
+$string['cliunknowoption'] = 'Таанылбаган параметрлер {$a}
+
+Сураныч, --help параметрин колдонуңуз';
+$string['cliyesnoprompt'] = 'У киргизиңиз (Ооба дегенди билдирет) же n киргизиңиз (Жок дегенди билдирет)';
+$string['environmentrequireinstall'] = 'Орнотуу жана күйгүзүү зарыл';
+$string['environmentrequireversion'] = '{$a->needed} версиясы талап кылынат, сиз колдонгон версия {$a->current}';
+$string['upgradekeyset'] = 'Жаӊыртуу ачкычы (аны көрсөткүӊүз келбесе бош калтырыӊыз)';
defined('MOODLE_INTERNAL') || die();
+$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Кыргызча';
--- /dev/null
+<?php
+// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['language'] = 'Тил';
+$string['moodlelogo'] = 'Moodle логотиби';
+$string['next'] = 'Мындан ары';
+$string['previous'] = 'Артка';
+$string['reload'] = 'Жаңыртуу';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Lietuvių';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thislanguage'] = 'Crnogorski';
defined('MOODLE_INTERNAL') || die();
-$string['cannotcreatelangdir'] = 'Не може да се креира lang директориумот.';
-$string['cannotcreatetempdir'] = 'Не може да се креира temp директориумот.';
-$string['cannotdownloadcomponents'] = 'Не може да се преземат компонентите.';
-$string['cannotdownloadzipfile'] = 'Не може да се преземи ZIP датотеката.';
-$string['cannotfindcomponent'] = 'Не може да се најде компонентата.';
-$string['cannotsavemd5file'] = 'Не може да се сними md5 датотеката.';
-$string['cannotsavezipfile'] = 'Не може да се сними ZIP датотеката.';
-$string['cannotunzipfile'] = 'Не може да се отпакува датотеката.';
-$string['componentisuptodate'] = 'Компонентата е од последната верзија';
-$string['downloadedfilecheckfailed'] = 'Проверката на преземената датотека не заврши успешно.';
-$string['invalidmd5'] = 'невалидна md5';
-$string['missingrequiredfield'] = 'Некои задолжителни полиња недостасуваат';
-$string['wrongdestpath'] = 'Погрешна дестинација.';
-$string['wrongsourcebase'] = 'Погрешна изворна адреса.';
-$string['wrongzipfilename'] = 'Погрешна ZIP датотека.';
+$string['cannotcreatedboninstall'] = '<p>Не може да се креира базата со податоци.</p>
+<p>Посочената база со податоци не постои и корисникот нема овластувања да ја креира базата со податоци.</p>
+<p>Администраторот на сајтот треба да ја верификува конфигурацијата на базата со податоци.</p>';
+$string['cannotcreatelangdir'] = 'Lang именик не може да се креира';
+$string['cannotcreatetempdir'] = 'Не може да се креира привремена папка';
+$string['cannotdownloadcomponents'] = 'Не можете да ги симнете компонентите';
+$string['cannotdownloadzipfile'] = 'Не можете да спуштите ZIP фајл';
+$string['cannotfindcomponent'] = 'Компонентата не е пронајдена';
+$string['cannotsavemd5file'] = 'Не може да се сними md5 фајлот';
+$string['cannotsavezipfile'] = 'Не може да се сними ZIP фајл';
+$string['cannotunzipfile'] = 'Не можеше да се одзипува фајлот';
+$string['componentisuptodate'] = 'Компонентата е ажурирана.';
+$string['dmlexceptiononinstall'] = '<p>Настана грешка во базата на податоци [{$a->errorcode}].<br />{$a->debuginfo}</p>';
+$string['downloadedfilecheckfailed'] = 'Проверката на преземениот фајл не успеа';
+$string['invalidmd5'] = 'Променливата за проверка беше погрешна - обидете се повторно';
+$string['missingrequiredfield'] = 'Некое задолжително поле недостасува';
+$string['remotedownloaderror'] = '<p>Преземањето на компонентата на вашиот сервер не успеа. Проверете ги поставките за прокси; се препорачува екстензијата PHP cURL.</p>
+<p>Мора да го рачно да го симнете фајлот <a href="{$a->url}">{$a->url}</a>, копирајте го "{$a->dest}" во вашиот сервер и таму отпакувајте го.</p>';
+$string['wrongdestpath'] = 'Погрешна дестинациска патека';
+$string['wrongsourcebase'] = 'Погрешна URL на извор';
+$string['wrongzipfilename'] = 'Погрешно име на ZIP фајл';
$string['moodlelogo'] = 'Moodle лого';
$string['next'] = 'Следно';
$string['previous'] = 'Претходно';
-$string['reload'] = 'Ð\92Ñ\87иÑ\82аÑ\98 повÑ\82оÑ\80но';
+$string['reload'] = 'Ð\92Ñ\87иÑ\82аÑ\98 одново';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Nederlands';
$string['admindirname'] = 'Adminmappe';
$string['availablelangs'] = 'Tilgjengelige språkpakker';
$string['chooselanguagehead'] = 'Velg et språk';
-$string['chooselanguagesub'] = 'Velg språk (bare for INSTALLASJONEN). Du vil kunne velge språk for nettsted og bruker på et skjermbilde senere.';
+$string['chooselanguagesub'] = 'Velg et språk for installasjonen. Dette språket vil også bli brukt som standardspråk for nettstedet, men det kan endres senere.';
$string['clialreadyconfigured'] = 'Konfigurasjonsfilen config.php finnes allerede. Vennligst bruk admin/cli/install_database.php hvis du vil installere Moodle på denne portalen.';
$string['clialreadyinstalled'] = 'Filen config.php eksisterer allerede. Vennligst bruk admin/cli/install_database.php hvis du vil oppgradere Moodle på denne portalen.';
$string['cliinstallheader'] = 'Moodle {$a} kommandolinje installasjonsprogram';
$string['environmentsub2'] = 'Hver Moodle-utgivelse har minimumskrav til PHP versjon og et sett med obligatoriske PHP-tillegg. En full sjekk av omgivelser gjøres før hver installering og oppgradering. Vær vennlig å kontakte serveradministrator hvis du ikke vet hvordan du skal installere nye versjoner eller slå på PHP-tillegg.';
$string['errorsinenvironment'] = 'Omgivelseskontroll feilet!';
$string['installation'] = 'Installasjon';
-$string['langdownloaderror'] = 'Beklageligvis ble ikke språkpakken "{$a}" installert. Installasjonsprosessen vil fortsette på Engelsk.';
+$string['langdownloaderror'] = 'Beklageligvis kunne ikke språkpakken "{$a}" lastes ned. Installasjonsprosessen vil fortsette på Engelsk.';
$string['memorylimithelp'] = '<p>PHP minnegrensen for serveren din er nå satt til {$a}.</p>
<p>Dette kan skape minneproblemer for Moodle senere, spesielt hvis du har mange moduler tillatt og/eller mange brukere.</p>
<p>Vi anbefaler at du konfigurer PHP med en høyere grense enn mulig, for eksepmel 40M. Det er flere måter å gjøre dette på.:</p>
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Norsk';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Gascon';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Occitan-Lengadocian';
$string['pathsrodataroot'] = 'A pasta de dados não tem permissões de escrita.';
$string['pathsroparentdataroot'] = 'A pasta ascendente <b>{$a->parent}</b> não tem permissões de escrita. O programa de instalação não conseguiu criar a pasta <b>{$a->dataroot}</b>.';
$string['pathssubadmindir'] = 'Alguns servidores Web utilizam a pasta <strong>admin</strong> em URLs especiais de acesso a funcionalidades especiais, como é o caso de painéis de controlo. Algumas situações podem criar conflitos com a localização normal das páginas de administração do Moodle. Estes problemas podem ser resolvidos renomeando a pasta <strong>admin</strong> na instalação do Moodle e indicando aqui o novo nome a utilizar. Por exemplo:<br /><br /><b>moodleadmin</b><br /><br />Esta ação resolverá os problemas de acesso das hiperligações para as funcionalidades de administração do Moodle.';
-$string['pathssubdataroot'] = '<p>Uma diretoria em que o Moodle irá armazenar todo o conteúdo de ficheiros enviados pelos utilizadores.</p>
+$string['pathssubdataroot'] = '<p>Diretoria onde o Moodle irá armazenar todo o conteúdo de ficheiros enviados pelos utilizadores.</p>
<p>Esta diretoria deve ser legível e gravável pelo utilizador do servidor web (geralmente \'www-data\', \'nobody\', ou \'apache\').</p>
<p>Não deve ser acessível diretamente através da web.</p>
<p>Se a diretoria não existir, o processo de instalação tentará criá-la.</p>';
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
$string['welcomep20'] = 'A apresentação desta página confirma a correta instalação e ativação do pacote <strong>{$a->packname} {$a->packversion}</strong> no servidor.';
$string['welcomep30'] = 'Esta versão do pacote <strong>{$a->installername}</strong> inclui as aplicações necessárias para o correto funcionamento do <strong>Moodle</strong>, nomeadamente:';
-$string['welcomep40'] = 'Este pacote inclui o lançamento <strong>{$a->moodlerelease} do Moodle ({$a->moodleversion})</strong>.';
+$string['welcomep40'] = 'Este pacote inclui o lançamento <strong>{$a->moodlerelease} ({$a->moodleversion})</strong> do Moodle .';
$string['welcomep50'] = 'A utilização de todas as aplicações incluídas neste pacote é limitada pelas respetivas licenças. O pacote completo <strong>{$a->installername}</strong> é <ahref="https://www.opensource.org/docs/definition_plain.html">código aberto</a> e é distribuído nos termos da licença <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
$string['welcomep60'] = 'As páginas seguintes irão acompanhá-lo através de algumas etapas simples para configurar e definir o <strong>Moodle</strong> no seu computador. Pode aceitar as configurações predefinidas ou, opcionalmente, alterá-las para adaptar às suas próprias necessidades.';
$string['welcomep70'] = 'Clique no botão "Seguinte" para continuar a configuração do <strong>Moodle</strong>.';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Português - Portugal';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Português - Brasil';
$string['errorsinenvironment'] = 'Verificarea mediului eșuată!';
$string['installation'] = 'Instalare';
$string['langdownloaderror'] = 'Din păcate, limba "{$a}" nu a putut fi descărcată. Procesul de instalare va continua în limba engleză.';
+$string['memorylimithelp'] = '<p> Limita de memorie PHP pentru serverul dvs. este setată în prezent la {$a}. </p>
+
+<p> Acest lucru poate determina Moodle să aibă probleme de memorie mai târziu, în special
+ dacă aveți o mulțime de module activate și / sau o mulțime de utilizatori. </p>
+
+<p> Vă recomandăm să configurați PHP cu o limită mai mare, dacă este posibil, cum ar fi 40M.
+ Există mai multe moduri de a face acest lucru pe care le puteți încerca: </p>
+<ol>
+<li> Dacă puteți, recompilați PHP cu <i> --enable-memory-limit </i>.
+ Aceasta va permite Moodle să stabilească singură limita de memorie. </li>
+<li> Dacă aveți acces la fișierul php.ini, puteți schimba <b> memory_limit </b>
+ stabilind acolo ceva de genul 40M. Dacă nu aveți acces, s-ar putea
+ să îi puteți cere administratorului să facă acest lucru pentru dvs. </li>
+<li> Pe unele servere PHP puteți crea un fișier .htaccess în directorul Moodle
+ care conține această linie:
+ <blockquote> <div> php_value memory_limit 40M </div> </blockquote>
+ <p> Cu toate acestea, pe unele servere, acest lucru va împiedica <b> toate </b> paginile PHP să funcționeze
+ (veți vedea erori când vă uitați la pagini), așa că va trebui să eliminați fișierul .htaccess. </p> </li>
+</ol>';
$string['paths'] = 'Căi';
$string['pathserrcreatedataroot'] = 'Data directory ({$a->dataroot}) nu poate fi creat de către installer.';
$string['pathshead'] = 'Confirmare căi';
$string['pathsrodataroot'] = 'Directorul dataroot nu poate fi scris.';
$string['pathsroparentdataroot'] = 'Directorul parent ({$a->parent}) nu poate fi scris. Directorul data ({$a->dataroot}) nu poate fi creat de persoana care îl instalează.';
+$string['pathssubadmindir'] = 'Foarte puțini webhosts folosesc / admin ca adresă URL specială pentru a accesa un
+panoul de control sau ceva de genul acesta. Din păcate, acest lucru intră în conflict cu locația standard pentru paginile de administrare Moodle. Puteți rezolva acest lucru
+redenumirea directorului de administrare din instalarea dvs. și plasarea acestui nou nume aici. De exemplu: <em> moodleadmin </em>. Aceasta va remedia linkurile de administrator din Moodle.';
$string['pathssubdataroot'] = '<p>Un director unde Moodle va stoca tot conținutul unui fișier încărcat de către utilizatori.</p>
<p>Acest director trebuie să poată fi citit și scris de către utilizatorii serverului web (de obicei \'www-data\', \'nobody\', or \'apache\').</p>
<p>Nu trebuie să fie direct accesibil de pe web.</p>
<p>Dacă directorul nu există în prezent, procesul de instalare va încerca să îl creeze.</p>';
$string['pathssubdirroot'] = '<p>Calea completă către directorul care conține codul Moodle .</p>';
+$string['pathssubwwwroot'] = '<p> Adresa completă unde va fi accesat Moodle, adică adresa pe care utilizatorii o vor introduce în bara de adrese a browserului lor pentru a accesa Moodle. </p>
+<p> Nu este posibil să accesați Moodle utilizând mai multe adrese. Dacă site-ul dvs. este accesibil prin mai multe adrese, alegeți-l pe cel mai simplu și configurați o redirecționare permanentă pentru fiecare dintre celelalte adrese. </p>
+<p> Dacă site-ul dvs. este accesibil atât de pe Internet, cât și de pe o rețea internă (uneori numită Intranet), atunci utilizați adresa publică aici. </p>
+<p> Dacă adresa curentă nu este corectă, vă rugăm să modificați adresa URL din bara de adrese a browserului dvs. și să reporniți instalarea. </p>';
$string['pathsunsecuredataroot'] = 'Locația dataroot nu este sigură';
$string['pathswrongadmindir'] = 'Directorul admin nu există';
$string['phpextension'] = 'extensie PHP {$a}';
$string['phpversion'] = 'Versiune PHP';
-$string['phpversionhelp'] = '<p>Moodle necesită o versiune PHP de cel puțin 4.3.0 sau 5.1.0 (5.0.x are un număr de probleme cunscute).</p>
-<p>Momentan rulați versiunea {$a}</p>
-<p>Trebuie să upgradați PHP sau să îl mutați pe o gazdă cu o nouă versiune de PHP!<br />
-(În cazul 5.0.x puteți, de asemenea, să downgradați la versiunea 4.4.x)</p>';
+$string['phpversionhelp'] = '<p> Moodle necesită o versiune PHP de cel puțin 5.6.5 sau 7.1 (7.0.x are unele limitări ale motorului). </p>
+<p> În prezent executați versiunea {$a}. </p>
+<p> Trebuie să faceți upgrade PHP sau să vă mutați la o gazdă cu o versiune mai nouă de PHP. </p>';
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
$string['welcomep20'] = 'Vedeți această pagină deoarece ați instalat și lansat cu succes pachetul <strong>{$a->packname} {$a->packversion}</strong> în computerul dumneavoastră. Felicitări!';
$string['welcomep30'] = 'Această versiune <strong>{$a->installername}</strong> include aplicații pentru a crea un mediu în care <strong>Moodle</strong> va funcționa, și anume:';
$string['welcomep40'] = 'Pachetul include și <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
-$string['welcomep50'] = 'Utilizarea tuturor aplicațiilor din acest pachet este determinată de respectivele lor
- licențe. Pachetul complet <strong>{$a->installername}</strong> este
- <a href="http://www.opensource.org/docs/definition_plain.html">open source</a> și este distribuit sub licența <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
+$string['welcomep50'] = 'Utilizarea tuturor aplicațiilor din acest pachet este guvernată de licențele respective. Pachetul complet <strong> {$a->installername} </strong> este <a href="https://www.opensource.org/docs/definition_plain.html"> open source </a> și este distribuit în licența <a href="https://www.gnu.org/copyleft/gpl.html"> GPL </a>.';
$string['welcomep60'] = 'Următoarele pagini vă oferă pași ușor de urmat pentru a
configura și seta <strong>Moodle</strong> în computerul dumneavoastră. Puteți accepta setările implicite
sau, opțional, să le modificați pentru a corespunde nevoilor dumneavoastră.';
defined('MOODLE_INTERNAL') || die();
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Română';
defined('MOODLE_INTERNAL') || die();
$string['clianswerno'] = 'n';
-$string['cliansweryes'] = 'y';
-$string['cliincorrectvalueretry'] = 'Valoarea nu este corectă, încercați din nou';
-$string['environmentrequireinstall'] = 'trebuie să fie instalat și activat';
-$string['environmentrequireversion'] = 'versiunea {$a->needed} este necesară iar dumneavoastră rulați {$a->current}';
+$string['cliansweryes'] = 'd';
+$string['cliincorrectvalueerror'] = 'Eroare, valoare incorectă "{$a->value}" pentru "{$a->option}"';
+$string['cliincorrectvalueretry'] = 'Valoare incorectă, încercaţi din nou';
+$string['clitypevalue'] = 'introduceţi valoarea';
+$string['clitypevaluedefault'] = 'introduceţi valoarea, apăsaţi Enter pentru a folosi valoarea implicită ({$a})';
+$string['cliunknowoption'] = 'Opţiuni necunoscute:
+ {$a}
+Vă rugăm folosiţi --opţiunea Ajutor.';
+$string['cliyesnoprompt'] = 'tastați d (pentru \'da\') sau \'n\' (pentru \'nu\')';
+$string['environmentrequireinstall'] = 'trebuie instalat şi activat';
+$string['environmentrequireversion'] = 'versiuna necesară este {$a->needed} în timp ce dumneavoastră rulaţi versiunea {$a->current}';
+$string['upgradekeyset'] = 'Actualizează cheie (lăsați gol pentru a nu fi setat)';
--- /dev/null
+<?php
+// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['cannotcreatedboninstall'] = '<p>Nu se poate crea baza de date.</p>
+<p>Baza de date specificată nu există și utilizatorul dat nu are permisiunea de a crea baza de date.</p>
+<p>Administratorul site-ului trebuie să verifice configurația bazei de date.</p>';
+$string['cannotcreatelangdir'] = 'Nu se poate crea directorul lang';
+$string['cannotcreatetempdir'] = 'Nu se poate crea directorul temporar';
+$string['cannotdownloadcomponents'] = 'Nu se pot descărca componente';
+$string['cannotdownloadzipfile'] = 'Nu se poate descărca fișierul ZIP';
+$string['cannotfindcomponent'] = 'Componenta nu poate fi găsită';
+$string['cannotsavemd5file'] = 'Nu se poate salva fișierul md5';
+$string['cannotsavezipfile'] = 'Nu se poate salva fișierul ZIP';
+$string['cannotunzipfile'] = 'Nu se poate dezarhiva fișierul';
+$string['componentisuptodate'] = 'Componenta este actualizată';
+$string['dmlexceptiononinstall'] = '<p>A apărut o eroare în baza de date [{$a->errorcode}].<br />{$a->debuginfo}</p>';
+$string['downloadedfilecheckfailed'] = 'Verificarea fișierului descărcat a eșuat';
+$string['invalidmd5'] = 'Variabila de verificare a fost greșită - încercați din nou';
+$string['missingrequiredfield'] = 'Un câmp obligatoriu lipsește';
+$string['remotedownloaderror'] = '<p>Descărcarea componentei pe serverul dvs. nu a reușit. Vă rugăm să verificați setările proxy; extensia PHP cURL este foarte recomandată.</p>
+<p>Trebuie să descărcați manual fișierul <a href="{$a->url}">{$a->url}</a>, copiați-l în „{$a->dest}" de pe server și dezarhivați-l Acolo.</p>';
+$string['wrongdestpath'] = 'Cale de destinație greșită';
+$string['wrongsourcebase'] = 'Baza de adrese URL sursă este greșită';
+$string['wrongzipfilename'] = 'Numele fișierului ZIP greșit';
--- /dev/null
+<?php
+// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package installer
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['admindirname'] = 'Director Admin';
+$string['availablelangs'] = 'Pachete de limbă disponibile';
+$string['chooselanguagehead'] = 'Selectare limbă';
+$string['chooselanguagesub'] = 'Vă rugăm selectaţi limba pentru interfaţa de instalare, limba selectată va fi folosită EXCLUSIV în cadrul procedurii de instalare. Ulterior veţi putea selecta limba în care doriţi să fie afişată interfaţa.';
+$string['clialreadyconfigured'] = 'Fișierul de configurare';
+$string['clialreadyinstalled'] = 'Fișierul de configurare config.php există deja. Vă rugăm să folosiți dmin/cli/install_database.php to pentru a upgrada Moodle pentru acest site.';
+$string['cliinstallheader'] = 'Program command line installation Moodle {$a}';
+$string['databasehost'] = 'Gazdă baza de date';
+$string['databasename'] = 'Nume baza de date';
+$string['databasetypehead'] = 'Alegere driver baza de date';
+$string['dataroot'] = 'Director date';
+$string['datarootpermission'] = 'Permisiuni directoare date';
+$string['dbprefix'] = 'Prefix tabele';
+$string['dirroot'] = 'Director Moodle';
+$string['environmenthead'] = 'Se verifică mediul...';
+$string['environmentsub2'] = 'Fiecare versiune Moodle are o anumită cerință minimă PHP și un număr de extensii PHP obligatorii.
+Verificarea completă a mediului se face înainte de fiecare instalare și upgrade. Vă rugăm să contactați administratorul serverului, dacă nu știți cum se instalează noua versiune sau dacă activați extensiile PHP.';
+$string['errorsinenvironment'] = 'Verificarea mediului eșuată!';
+$string['installation'] = 'Instalare';
+$string['langdownloaderror'] = 'Din păcate, limba "{$a}" nu a putut fi descărcată. Procesul de instalare va continua în limba engleză.';
+$string['paths'] = 'Căi';
+$string['pathserrcreatedataroot'] = 'Data directory ({$a->dataroot}) nu poate fi creat de către installer.';
+$string['pathshead'] = 'Confirmare căi';
+$string['pathsrodataroot'] = 'Directorul dataroot nu poate fi scris.';
+$string['pathsroparentdataroot'] = 'Directorul parent ({$a->parent}) nu poate fi scris. Directorul data ({$a->dataroot}) nu poate fi creat de persoana care îl instalează.';
+$string['pathssubdataroot'] = '<p>Un director unde Moodle va stoca tot conținutul unui fișier încărcat de către utilizatori.</p>
+<p>Acest director trebuie să poată fi citit și scris de către utilizatorii serverului web (de obicei \'www-data\', \'nobody\', or \'apache\').</p>
+<p>Nu trebuie să fie direct accesibil de pe web.</p>
+<p>Dacă directorul nu există în prezent, procesul de instalare va încerca să îl creeze.</p>';
+$string['pathssubdirroot'] = '<p>Calea completă către directorul care conține codul Moodle .</p>';
+$string['pathsunsecuredataroot'] = 'Locația dataroot nu este sigură';
+$string['pathswrongadmindir'] = 'Directorul admin nu există';
+$string['phpextension'] = 'extensie PHP {$a}';
+$string['phpversion'] = 'Versiune PHP';
+$string['phpversionhelp'] = '<p>Moodle necesită o versiune PHP de cel puțin 4.3.0 sau 5.1.0 (5.0.x are un număr de probleme cunscute).</p>
+<p>Momentan rulați versiunea {$a}</p>
+<p>Trebuie să upgradați PHP sau să îl mutați pe o gazdă cu o nouă versiune de PHP!<br />
+(În cazul 5.0.x puteți, de asemenea, să downgradați la versiunea 4.4.x)</p>';
+$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
+$string['welcomep20'] = 'Vedeți această pagină deoarece ați instalat și lansat cu succes pachetul <strong>{$a->packname} {$a->packversion}</strong> în computerul dumneavoastră. Felicitări!';
+$string['welcomep30'] = 'Această versiune <strong>{$a->installername}</strong> include aplicații pentru a crea un mediu în care <strong>Moodle</strong> va funcționa, și anume:';
+$string['welcomep40'] = 'Pachetul include și <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
+$string['welcomep50'] = 'Utilizarea tuturor aplicațiilor din acest pachet este determinată de respectivele lor
+ licențe. Pachetul complet <strong>{$a->installername}</strong> este
+ <a href="http://www.opensource.org/docs/definition_plain.html">open source</a> și este distribuit sub licența <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
+$string['welcomep60'] = 'Următoarele pagini vă oferă pași ușor de urmat pentru a
+ configura și seta <strong>Moodle</strong> în computerul dumneavoastră. Puteți accepta setările implicite
+ sau, opțional, să le modificați pentru a corespunde nevoilor dumneavoastră.';
+$string['welcomep70'] = 'Click pe butonul "Next" de mai jos pentru a continua setarea <strong>Moodle</strong>.';
+$string['wwwroot'] = 'Adresă Web';
defined('MOODLE_INTERNAL') || die();
$string['parentlanguage'] = 'ro';
-$string['thislanguage'] = 'Workplace în limba română';
+$string['thisdirection'] = 'ltr';
+$string['thislanguage'] = 'Română Workplace';
$string['language'] = 'Limbă';
$string['moodlelogo'] = 'Logo Moodle';
-$string['next'] = 'Următoarea';
+$string['next'] = 'Următorul';
$string['previous'] = 'Anterior';
$string['reload'] = 'Reîncarcă';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = 'Slovenščina';
$string['clianswerno'] = 'n';
$string['cliansweryes'] = 'y';
$string['cliincorrectvalueerror'] = 'Fel, värdet "{$a->value}" för "{$a->option}" är inte korrekt.';
-$string['cliincorrectvalueretry'] = 'Felaktigt värde, v.g. försök igen';
+$string['cliincorrectvalueretry'] = 'Felaktigt värde, försök igen';
$string['clitypevalue'] = 'Värde för typ';
$string['clitypevaluedefault'] = 'skriv in värdet, klicka på "Enter" om du vill använda standardvärdet ({$a})';
-$string['cliunknowoption'] = 'Ej identifierade alternativ: {$a} V.g. använd alternativet Hjälp.';
+$string['cliunknowoption'] = 'Ej identifierade alternativ:
+ {$a}
+Vänligen använd --help alternativet.';
$string['cliyesnoprompt'] = 'skriv in y (betyder ja) eller n (betyder nej)';
-$string['environmentrequireinstall'] = 'är nödvändig att installera/aktivera';
-$string['environmentrequireversion'] = 'version {$a->needed} är nödvändig och du använder {$a->current}';
+$string['environmentrequireinstall'] = 'måste vara installerat och aktiverat';
+$string['environmentrequireversion'] = 'version {$a->needed} krävs och du använder {$a->current}';
$string['downloadedfilecheckfailed'] = 'Det gick inte att kontrollera den nedladdade filen';
$string['invalidmd5'] = 'Kontrollvariabeln var felaktig - försök igen';
$string['missingrequiredfield'] = 'Det saknas några obligatoriska fält';
-$string['remotedownloaderror'] = 'Nedladdningen av en komponent till din server misslyckades, var snäll och verifiera inställningar för proxy. <br /><br />Du måste ladda ner <a href="{$a->url}">{$a->url}</a> filen manuellt, kopiera den till "{$a->dest}" på din server och packa upp den där.';
+$string['remotedownloaderror'] = 'Nedladdningen av en komponent till din server misslyckades. Vänligen verifiera inställningar för proxy. <br /><br />Du måste ladda ner <a href="{$a->url}">{$a->url}</a> filen manuellt, kopiera den till "{$a->dest}" på din server och packa upp den där.';
$string['wrongdestpath'] = 'Fel sökväg';
$string['wrongsourcebase'] = 'Fel bas-URL till källan';
$string['wrongzipfilename'] = 'Fel namn på ZIP-filen';
$string['admindirname'] = 'Katalog/mapp för administration';
$string['availablelangs'] = 'Tillgängliga språkpaket';
$string['chooselanguagehead'] = 'Välj ett språk';
-$string['chooselanguagesub'] = 'Var snäll och välj ett språk för installationen. Du kommer att ha möjlighet att välja språk för webbplatsen och användarna på en senare skärm.';
-$string['clialreadyinstalled'] = 'Filen config.php finns redan, var snäll och använd admin/cli/upgrade.php om Du vill uppgradera Din webbplats.';
+$string['chooselanguagesub'] = 'Vänligen välj ett språk för installationen. Du kommer att ha möjlighet att välja språk för webbplatsen och användarna på en senare skärm.';
+$string['clialreadyinstalled'] = 'Filen config.php finns redan. Vänligen använd admin/cli/upgrade.php om Du vill uppgradera Din webbplats.';
$string['cliinstallheader'] = 'Installationsprogram av typ kommandorad {$a} för Moodle ';
$string['databasehost'] = 'Värd för databas';
$string['databasename'] = 'Namn på databas';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = '简体中文';
defined('MOODLE_INTERNAL') || die();
-// Warning: this parentlanguage value is not a valid language code!
-// $string['parentlanguage'] = '';
+$string['parentlanguage'] = '';
$string['thisdirection'] = 'ltr';
$string['thislanguage'] = '正體中文';
$this->assertEquals('array', gettype($result));
$this->assertEquals('San Francisco', $result['city']);
- $this->assertEqualsWithDelta(-122.3933, $result['longitude'], 0.01, 'Coordinates are out of accepted tolerance');
- $this->assertEqualsWithDelta(37.7697, $result['latitude'], 0.01, 'Coordinates are out of accepted tolerance');
+ $this->assertEqualsWithDelta(-122.3933, $result['longitude'], 0.1, 'Coordinates are out of accepted tolerance');
+ $this->assertEqualsWithDelta(37.7697, $result['latitude'], 0.1, 'Coordinates are out of accepted tolerance');
$this->assertNull($result['error']);
$this->assertEquals('array', gettype($result['title']));
$this->assertEquals('San Francisco', $result['title'][0]);
$string['configsessioncookiedomain'] = 'This allows you to change the domain that the Moodle cookies are available from. This is useful for Moodle customisations (e.g. authentication or enrolment plugins) that need to share Moodle session information with a web application on another subdomain. <strong>WARNING: it is strongly recommended to leave this setting at the default (empty) - an incorrect value will prevent all logins to the site.</strong>';
$string['configsessioncookiepath'] = 'If you need to change where browsers send the Moodle cookies, you can change this setting to specify a subdirectory of your web site. Otherwise the default \'/\' should be fine.';
$string['configsessiontimeout'] = 'If people logged in to this site are idle for a long time (without loading pages) then they are automatically logged out (their session is ended). This variable specifies how long this time should be.';
+$string['configsessiontimeoutwarning'] = 'If people logged in to this site are idle for a long time (without loading pages) then they are warned about their session is about to end. This variable specifies how long this time should be.';
+$string['configsessiontimeoutwarningcheck'] = 'Session timeout warning must be less than session timeout';
$string['configshowicalsource'] = 'Show source information for iCal events';
$string['configshowcommentscount'] = 'Show comments count, it will cost one more query when display comments link';
$string['configshowsiteparticipantslist'] = 'All of these site students and site teachers will be listed on the site participants list. Who shall be allowed to see this site participants list?';
$string['divertallemails'] = 'Email diverting';
$string['divertallemailsdetail'] = 'Used as a safeguard in development environments when testing emails and should not be used in production.';
$string['divertallemailsexcept'] = 'Email diversion exceptions';
-$string['divertallemailsexcept_desc'] = 'A list of email exception rules separated by either commas or new lines. Each rule is interpreted as a regular expression, eg<pre>simone@acme.com
+$string['divertallemailsexcept_desc'] = 'A list of email exception rules separated by either commas or new lines. Each rule is interpreted as a regular expression e.g. <pre>simone@acme.com
.*@acme.com
fred(\\+.*)?@acme.com
</pre>';
$string['enableuserfeedback_desc'] = 'If enabled, a \'Give feedback about this software\' link is displayed in the footer for users to give feedback about the Moodle software to Moodle HQ. If the \'Next feedback reminder\' option is set, the user is also shown a reminder on the Dashboard at the specified interval. Setting \'Next feedback reminder\' to \'Never\' disables the Dashboard reminder, while leaving the \'Give feedback about this software\' link in the footer.';
$string['enablewebservices'] = 'Enable web services';
$string['enablewsdocumentation'] = 'Web services documentation';
+$string['encryptedpassword_set'] = '(Set and encrypted)';
+$string['encryptedpassword_edit'] = 'Enter new value';
$string['enrolinstancedefaults'] = 'Enrolment instance defaults';
$string['enrolinstancedefaults_desc'] = 'Default enrolment settings in new courses.';
$string['enrolmultipleusers'] = 'Enrol the users';
$string['sessioncookiepath'] = 'Cookie path';
$string['sessionhandling'] = 'Session handling';
$string['sessiontimeout'] = 'Timeout';
+$string['sessiontimeoutwarning'] = 'Timeout Warning';
$string['settingdependenton'] = 'This setting may be hidden, based on the value of <strong>{$a}</strong>.';
$string['settingfileuploads'] = 'File uploading is required for normal operation, please enable it in PHP configuration.';
$string['settingmemorylimit'] = 'Insufficient memory detected, please set higher memory limit in PHP settings.';
$string['analyticslogstore_help'] = 'The log store that will be used by the analytics API to read users\' activity.';
$string['analyticssettings'] = 'Analytics settings';
$string['analyticssiteinfo'] = 'Site information';
+$string['calclifetime'] = 'Keep analytics calculations for';
+$string['configlcalclifetime'] = 'This specifies the length of time you want to keep calculation data - this will not delete predictions, but deletes the data used to generate the predictions. Using the default option here is best as it keeps your disk usage under control, however if you are using calculation tables for other purposes you may want to increase this value.';
$string['defaulttimesplittingmethods'] = 'Default analysis intervals for model\'s evaluation';
$string['defaulttimesplittingmethods_help'] = 'The analysis interval defines when the system will calculate predictions and the portion of activity logs that will be considered for those predictions. The model evaluation process will iterate through these analysis intervals unless a specific analysis interval is specified.';
$string['defaultpredictionsprocessor'] = 'Default predictions processor';
$string['modeltimelimit'] = 'Analysis time limit per model';
$string['modeltimelimitinfo'] = 'This setting limits the time each model spends analysing the site contents.';
$string['neutral'] = 'Neutral';
+$string['neverdelete'] = 'Never delete calculations';
$string['noevaluationbasedassumptions'] = 'Models based on assumptions cannot be evaluated.';
$string['nodata'] = 'No data to analyse';
$string['noinsightsmodel'] = 'This model does not generate insights';
$string['backpacksettings'] = 'Backpack settings';
$string['backpackapiurl'] = 'Backpack API URL';
$string['backpackweburl'] = 'Backpack URL';
-$string['backpackprovider'] = 'Backpack Provider';
+$string['backpackprovider'] = 'Backpack provider';
$string['badges'] = 'Badges';
$string['badgedetails'] = 'Badge details';
$string['badgeimage'] = 'Image';
$string['cachedef_user_group_groupings'] = 'User\'s groupings and groups per course';
$string['cachedef_user_course_content_items'] = 'User\'s content items (activities, resources and their subtypes) per course';
$string['cachedef_yuimodules'] = 'YUI Module definitions';
+$string['cachedef_gradesetting'] = 'Course grade setting';
$string['cachelock_file_default'] = 'Default file locking';
$string['cachestores'] = 'Cache stores';
$string['canuselocalstore'] = 'Can use local store';
$string['duplicateroleshortname'] = 'There is already a role with this short name!';
$string['duplicateusername'] = 'Duplicate username - skipping record';
$string['emailfail'] = 'Emailing failed';
+$string['encryption_encryptfailed'] = 'Encryption failed';
+$string['encryption_decryptfailed'] = 'Decryption failed';
+$string['encryption_invalidkey'] = 'Invalid key';
+$string['encryption_keyalreadyexists'] = 'Key already exists';
+$string['encryption_nokey'] = 'Key not found';
+$string['encryption_wrongmethod'] = 'Data does not match a supported encryption method';
$string['enddatebeforestartdate'] = 'The course end date must be after the start date.';
$string['error'] = 'Error occurred';
$string['error_question_answers_missing_in_db'] = 'Failed to find an answer matching "{$a->answer}" in the question_answers database table. This occurred while restoring the question with id {$a->filequestionid} in the backup file, which has been matched to the existing question with id {$a->dbquestionid} in the database.';
$string['coursesiamteaching'] = 'Courses I am teaching';
$string['coursescales'] = 'Course scales';
$string['coursesettings'] = 'Course settings';
-$string['coursesettingsexplanation'] = 'Course settings determine how the gradebook appears for all participants in the course.';
+$string['coursesettingsexplanation'] = 'Course grade settings determine how the gradebook appears for all participants in the course.';
$string['coursetotal'] = 'Course total';
$string['createcategory'] = 'Create category';
$string['createcategoryerror'] = 'Could not create a new category';
<p>All you need to do is make up a username and password and use it in the form on this page!</p>
<p>If someone else has already chosen your username then you\'ll have to try again using a different username.</p>';
$string['loginto'] = 'Log in to {$a}';
+$string['loginagain'] = 'Log in again';
$string['logout'] = 'Log out';
$string['logoutconfirm'] = 'Do you really want to log out?';
$string['logs'] = 'Logs';
$string['accountidnumber'] = 'ID number';
$string['accountidnumber_help'] = 'The ID number is only used when matching the account against external systems and is not displayed anywhere on the site. If the account has an official code name it may be entered, otherwise the field can be left blank.';
$string['accountname'] = 'Account name';
-$string['accountname_help'] = 'How this account will be identified for teachers or managers who set up payments (for example in the course enrolment plugin)';
+$string['accountname_help'] = 'How this account will be identified for teachers or managers who set up payments (for example in the course enrolment plugin).';
$string['accountnotavailable'] = 'Not available';
-$string['paymentaccountsexplained'] = 'Create one or multiple payment accounts for this site. Each account includes configuration for available payment gateways. The person who configures payments on the site (for example, payment for the course enrolment) will be able to chose from the available accounts.';
+$string['paymentaccountsexplained'] = 'Create one or multiple payment accounts for this site. Each account includes configuration for available payment gateways. The person who configures payments on the site (for example, payment for the course enrolment) will be able to choose from the available accounts.';
$string['createaccount'] = 'Create payment account';
$string['deleteorarchive'] = 'Delete or archive';
$string['eventaccountcreated'] = 'Payment account created';
}
}
+/**
+ * Admin setting class for encrypted values using secure encryption.
+ *
+ * @copyright 2019 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class admin_setting_encryptedpassword extends admin_setting {
+
+ /**
+ * Constructor. Same as parent except that the default value is always an empty string.
+ *
+ * @param string $name Internal name used in config table
+ * @param string $visiblename Name shown on form
+ * @param string $description Description that appears below field
+ */
+ public function __construct(string $name, string $visiblename, string $description) {
+ parent::__construct($name, $visiblename, $description, '');
+ }
+
+ public function get_setting() {
+ return $this->config_read($this->name);
+ }
+
+ public function write_setting($data) {
+ $data = trim($data);
+ if ($data === '') {
+ // Value can really be set to nothing.
+ $savedata = '';
+ } else {
+ // Encrypt value before saving it.
+ $savedata = \core\encryption::encrypt($data);
+ }
+ return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
+ }
+
+ public function output_html($data, $query='') {
+ global $OUTPUT;
+
+ $default = $this->get_defaultsetting();
+ $context = (object) [
+ 'id' => $this->get_id(),
+ 'name' => $this->get_full_name(),
+ 'set' => $data !== '',
+ 'novalue' => $this->get_setting() === null
+ ];
+ $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
+
+ return format_admin_setting($this, $this->visiblename, $element, $this->description,
+ true, '', $default, $query);
+ }
+}
+
/**
* Empty setting used to allow flags (advanced) on settings that can have no sensible default.
* Note: Only advanced makes sense right now - locked does not.
/** @var int default duration unit */
protected $defaultunit;
+ /** @var callable|null Validation function */
+ protected $validatefunction = null;
/**
* Constructor
parent::__construct($name, $visiblename, $description, $defaultsetting);
}
+ /**
+ * Sets a validate function.
+ *
+ * The callback will be passed one parameter, the new setting value, and should return either
+ * an empty string '' if the value is OK, or an error message if not.
+ *
+ * @param callable|null $validatefunction Validate function or null to clear
+ * @since Moodle 3.10
+ */
+ public function set_validate_function(?callable $validatefunction = null) {
+ $this->validatefunction = $validatefunction;
+ }
+
+ /**
+ * Validate the setting. This uses the callback function if provided; subclasses could override
+ * to carry out validation directly in the class.
+ *
+ * @param int $data New value being set
+ * @return string Empty string if valid, or error message text
+ * @since Moodle 3.10
+ */
+ protected function validate_setting(int $data): string {
+ // If validation function is specified, call it now.
+ if ($this->validatefunction) {
+ return call_user_func($this->validatefunction, $data);
+ } else {
+ return '';
+ }
+ }
+
/**
* Returns selectable units.
* @static
return get_string('errorsetting', 'admin');
}
+ // Validate the new setting.
+ $error = $this->validate_setting($seconds);
+ if ($error) {
+ return $error;
+ }
+
$result = $this->config_write($this->name, $seconds);
return ($result ? '' : get_string('errorsetting', 'admin'));
}