**/yui/build/** -diff
**/amd/build/** -diff
+lib/dml/oci_native_moodle_package.sql text eol=lf
theme/bootstrapbase/style/editor.css -diff
theme/bootstrapbase/style/moodle.css -diff
- mysql-client-core-5.6
- mysql-client-5.6
-services:
- - redis-server
+# Redis tests are currently failing on php 7.2 due to https://bugs.php.net/bug.php?id=75628
+# services:
+# - redis-server
env:
# Although we want to run these jobs and see failures as quickly as possible, we also want to get the slowest job to
fi
# Enable Redis.
- echo 'extension="redis.so"' > /tmp/redis.ini
- phpenv config-add /tmp/redis.ini
+ # Redis tests are currently failing on php 7.2 due to https://bugs.php.net/bug.php?id=75628
+ # echo 'extension="redis.so"' > /tmp/redis.ini
+ # phpenv config-add /tmp/redis.ini
# Install composer dependencies.
# We need --no-interaction in case we hit API limits for composer. This causes it to fall back to a standard clone.
mkdir -p "$HOME"/roots/phpunit
# The phpunit dataroot and prefix..
+ # Redis tests are currently failing on php 7.2 due to https://bugs.php.net/bug.php?id=75628
+ # -e "/require_once/i \\define('TEST_SESSION_REDIS_HOST', '127.0.0.1');" \
sed -i \
-e "/require_once/i \\\$CFG->phpunit_dataroot = '\/home\/travis\/roots\/phpunit';" \
-e "/require_once/i \\\$CFG->phpunit_prefix = 'p_';" \
- -e "/require_once/i \\define('TEST_SESSION_REDIS_HOST', '127.0.0.1');" \
config.php ;
# Initialise PHPUnit for Moodle.
// If custom field then pick name from database.
$fieldshortname = str_replace('profile_field_', '', $fieldname);
$fieldname = $customfieldname[$fieldshortname]->name;
+ if (core_text::strlen($fieldshortname) > 67) {
+ // If custom profile field name is longer than 67 characters we will not be able to store the setting
+ // such as 'field_updateremote_profile_field_NOTSOSHORTSHORTNAME' in the database because the character
+ // limit for the setting name is 100.
+ continue;
+ }
} elseif ($fieldname == 'url') {
$fieldname = get_string('webpage');
} else {
echo "Redeleting user $user->id: $user->username ($user->email)\n";
delete_user($user);
}
+$rs->close();
cli_heading('Deleting all leftovers');
</CUSTOM_CHECK>
</CUSTOM_CHECKS>
</MOODLE>
+ <MOODLE version="3.5" requires="3.1">
+ <UNICODE level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unicoderequired" />
+ </FEEDBACK>
+ </UNICODE>
+ <DATABASE level="required">
+ <VENDOR name="mariadb" version="5.5.31" />
+ <VENDOR name="mysql" version="5.5.31" />
+ <VENDOR name="postgres" version="9.3" />
+ <VENDOR name="mssql" version="10.0" />
+ <VENDOR name="oracle" version="10.2" />
+ </DATABASE>
+ <PHP version="7.0.0" level="required">
+ </PHP>
+ <PCREUNICODE level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="pcreunicodewarning" />
+ </FEEDBACK>
+ </PCREUNICODE>
+ <PHP_EXTENSIONS>
+ <PHP_EXTENSION name="iconv" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="iconvrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="mbstring" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="mbstringrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="curl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="curlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="openssl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="opensslrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="tokenizer" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="tokenizerrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlrpc" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="xmlrpcrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="soap" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="soaprecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="ctype" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ctyperequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zip" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ziprequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zlib" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="gd" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="gdrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="simplexml" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="simplexmlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="spl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="splrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="pcre" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="dom" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xml" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlreader" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="intl" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="intlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="json" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="hash" level="required"/>
+ <PHP_EXTENSION name="fileinfo" level="required"/>
+ </PHP_EXTENSIONS>
+ <PHP_SETTINGS>
+ <PHP_SETTING name="memory_limit" value="96M" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="settingmemorylimit" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="file_uploads" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingfileuploads" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="opcache.enable" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="opcacherecommended" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ </PHP_SETTINGS>
+ <CUSTOM_CHECKS>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_storage_engine" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddbstorageengine" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="question/engine/upgrade/upgradelib.php" function="quiz_attempts_upgraded" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="quizattemptsupgradedmessage" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_slasharguments" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="slashargumentswarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_database_tables_row_format" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="unsupporteddbtablerowformat" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_unoconv_version" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="unoconvwarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_libcurl_version" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="libcurlwarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_format" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddbfileformat" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_file_per_table" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddbfilepertable" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_large_prefix" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unsupporteddblargeprefix" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_is_https" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="ishttpswarning" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ <CUSTOM_CHECK file="lib/upgradelib.php" function="check_mysql_incomplete_unicode_support" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="incompleteunicodesupport" />
+ </FEEDBACK>
+ </CUSTOM_CHECK>
+ </CUSTOM_CHECKS>
+ </MOODLE>
</COMPATIBILITY_MATRIX>
admin_externalpage_setup('registrationmoodleorg');
if ($url !== HUB_MOODLEORGHUBURL) {
+ // Allow other plugins to confirm registration on hubs other than moodle.net . Plugins implementing this
+ // callback need to redirect or exit. See https://docs.moodle.org/en/Hub_registration .
+ $callbacks = get_plugins_with_function('hub_registration');
+ foreach ($callbacks as $plugintype => $plugins) {
+ foreach ($plugins as $plugin => $callback) {
+ $callback('confirm');
+ }
+ }
throw new moodle_exception('errorotherhubsnotsupported', 'hub');
}
admin_externalpage_setup('registrationmoodleorg');
if ($url !== HUB_MOODLEORGHUBURL) {
+ // Allow other plugins to renew registration on hubs other than moodle.net . Plugins implementing this
+ // callback need to redirect or exit. See https://docs.moodle.org/en/Hub_registration .
+ $callbacks = get_plugins_with_function('hub_registration');
+ foreach ($callbacks as $plugintype => $plugins) {
+ foreach ($plugins as $plugin => $callback) {
+ $callback('renew');
+ }
+ }
throw new moodle_exception('errorotherhubsnotsupported', 'hub');
}
--- /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/>.
+
+/**
+ * Helper class that contains helper functions for cli scripts.
+ *
+ * @package tool_analytics
+ * @copyright 2017 onwards Ankit Agarwal
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_analytics;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Helper class that contains helper functions for cli scripts.
+ *
+ * @package tool_analytics
+ * @copyright 2017 onwards Ankit Agarwal
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class clihelper {
+
+ /**
+ * List all models in the system. To be used from cli scripts.
+ *
+ * @return void
+ */
+ public static function list_models() {
+ cli_heading("List of models");
+ echo str_pad(get_string('modelid', 'tool_analytics'), 15, ' ') . ' ' . str_pad(get_string('name'), 50, ' ') .
+ ' ' . str_pad(get_string('status'), 15, ' ') . "\n";
+ $models = \core_analytics\manager::get_all_models();
+ foreach ($models as $model) {
+ $modelid = $model->get_id();
+ $isenabled = $model->is_enabled() ? get_string('enabled', 'tool_analytics') : get_string('disabled', 'tool_analytics');
+ $name = $model->get_target()->get_name();
+ echo str_pad($modelid, 15, ' ') . ' ' . str_pad($name, 50, ' ') . ' ' . str_pad($isenabled, 15, ' ') . "\n";
+ }
+ }
+}
\ No newline at end of file
debugging("The time splitting method '{$modeldata->timesplitting}' should include a '{$identifier}_help'
string to describe its purpose.", DEBUG_DEVELOPER);
}
+ } else {
+ $helpicon = new \help_icon('timesplittingnotdefined', 'tool_analytics');
+ $modeldata->timesplittinghelp = $helpicon->export_for_template($output);
}
// Has this model generated predictions?.
}
// Enable / disable.
- if ($model->is_enabled()) {
- $action = 'disable';
- $text = get_string('disable');
- $icontype = 't/block';
- } else {
- $action = 'enable';
- $text = get_string('enable');
- $icontype = 'i/checked';
+ if ($model->is_enabled() || !empty($modeldata->timesplitting)) {
+ // If there is no timesplitting method set, the model can not be enabled.
+ if ($model->is_enabled()) {
+ $action = 'disable';
+ $text = get_string('disable');
+ $icontype = 't/block';
+ } else {
+ $action = 'enable';
+ $text = get_string('enable');
+ $icontype = 'i/checked';
+ }
+ $urlparams['action'] = $action;
+ $url = new \moodle_url('model.php', $urlparams);
+ $icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text);
+ $actionsmenu->add($icon);
}
- $urlparams['action'] = $action;
- $url = new \moodle_url('model.php', $urlparams);
- $icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text);
- $actionsmenu->add($icon);
// Export training data.
if (!$model->is_static() && $model->is_trained()) {
Options:
--modelid Model id
+--list List models
--timesplitting Time splitting method full class name
-h, --help Print out this help
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
+ 'list' => false,
'modelid' => false,
'timesplitting' => false
),
exit(0);
}
-if ($options['modelid'] === false || $options['timesplitting'] === false) {
+if ($options['list'] || $options['modelid'] === false) {
+ \tool_analytics\clihelper::list_models();
+ exit(0);
+}
+
+if ($options['timesplitting'] === false) {
echo $help;
exit(0);
}
Options:
--modelid Model id
+--list List models
--non-interactive Not interactive questions
--timesplitting Restrict the evaluation to 1 single time splitting method (Optional)
--filter Analyser dependant. e.g. A courseid would evaluate the model using a single course (Optional)
array(
'help' => false,
'modelid' => false,
+ 'list' => false,
'timesplitting' => false,
'reuse-prev-analysed' => true,
'non-interactive' => false,
exit(0);
}
-if ($options['modelid'] === false) {
- echo $help;
+if ($options['list'] || $options['modelid'] === false) {
+ \tool_analytics\clihelper::list_models();
exit(0);
}
$string['clienablemodel'] = 'You can enable the model by selecting a time-splitting method by its ID. Note that you can also enable it later using the web interface (\'none\' to exit).';
$string['clievaluationandpredictions'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. You can allow these processes to be executed manually via the web interface by disabling the <a href="{$a}">\'onlycli\'</a> analytics setting.';
$string['clievaluationandpredictionsnoadmin'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. It may be enabled by a site administrator.';
+$string['disabled'] = 'Disabled';
$string['editmodel'] = 'Edit "{$a}" model';
$string['edittrainedwarning'] = 'This model has already been trained. Note that changing its indicators or its time-splitting method will delete its previous predictions and start generating new predictions.';
$string['enabled'] = 'Enabled';
$string['invalidprediction'] = 'Invalid to get predictions';
$string['invalidtraining'] = 'Invalid to train the model';
$string['loginfo'] = 'Log extra info';
+$string['modelid'] = 'Model id';
$string['modelinvalidanalysables'] = 'Invalid analysable elements for "{$a}" model';
$string['modelresults'] = '{$a} results';
$string['modeltimesplitting'] = 'Time splitting';
$string['samestartdate'] = 'Current start date is good';
$string['sameenddate'] = 'Current end date is good';
$string['target'] = 'Target';
+$string['timesplittingnotdefined'] = 'Time splitting is not defined.';
+$string['timesplittingnotdefined_help'] = 'You need to select a time-splitting method before enabling the model.';
$string['trainandpredictmodel'] = 'Training model and calculating predictions';
$string['trainingprocessfinished'] = 'Training process finished';
$string['trainingresults'] = 'Training results';
{{/timesplitting}}
{{^timesplitting}}
{{#str}}notdefined, tool_analytics{{/str}}
+ {{#timesplittinghelp}}
+ {{>core/help_icon}}
+ {{/timesplittinghelp}}
{{/timesplitting}}
</td>
<td>
// Show process name in first row.
foreach ($processes as $name => $process) {
// If we don't have enough space to show full run name then show runX.
- if ($lengthofprocessline < strlen($name + 2)) {
+ if ($lengthofprocessline < strlen($name) + 2) {
$name = substr($name, -5);
}
// One extra padding as we are adding | separator for rest of the data.
function xmldb_tool_customlang_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
"Test image from another site should be replaced" => [
"content" => '<img src="' . $this->getExternalTestFileUrl('/test.jpg', false) . '">',
"outputregex" => '/UPDATE/',
- "expectedcontent" => '<img src="' . $this->getExternalTestFileUrl('/test.jpg', true) . '">',
+ "expectedcontent" => '<img src="' . $this->get_converted_http_link('/test.jpg') . '">',
],
"Test object from another site should be replaced" => [
"content" => '<object data="' . $this->getExternalTestFileUrl('/test.swf', false) . '">',
"outputregex" => '/UPDATE/',
- "expectedcontent" => '<object data="' . $this->getExternalTestFileUrl('/test.swf', true) . '">',
+ "expectedcontent" => '<object data="' . $this->get_converted_http_link('/test.swf') . '">',
],
"Test image from a site with international name should be replaced" => [
"content" => '<img src="http://中国互联网络信息中心.中国/logosy/201706/W01.png">',
"Search for params should be case insensitive" => [
"content" => '<object DATA="' . $this->getExternalTestFileUrl('/test.swf', false) . '">',
"outputregex" => '/UPDATE/',
- "expectedcontent" => '<object DATA="' . $this->getExternalTestFileUrl('/test.swf', true) . '">',
+ "expectedcontent" => '<object DATA="' . $this->get_converted_http_link('/test.swf') . '">',
],
"URL should be case insensitive" => [
"content" => '<object data="HTTP://some.site/path?query">',
"content" => '<img alt="A picture" src="' . $this->getExternalTestFileUrl('/test.png', false) .
'" width="1”><p style="font-size: \'20px\'"></p>',
"outputregex" => '/UPDATE/',
- "expectedcontent" => '<img alt="A picture" src="' . $this->getExternalTestFileUrl('/test.png', true) .
+ "expectedcontent" => '<img alt="A picture" src="' . $this->get_converted_http_link('/test.png') .
'" width="1”><p style="font-size: \'20px\'"></p>',
],
"Broken URL should not be changed" => [
$this->getExternalTestFileUrl('/test.jpg', false) . '"></a>',
"outputregex" => '/UPDATE/',
"expectedcontent" => '<a href="' . $this->getExternalTestFileUrl('/test.png', false) . '"><img src="' .
- $this->getExternalTestFileUrl('/test.jpg', true) . '"></a>',
+ $this->get_converted_http_link('/test.jpg') . '"></a>',
],
];
}
+ /**
+ * Convert the HTTP external test file URL to use HTTPS.
+ *
+ * Note: We *must not* use getExternalTestFileUrl with the True option
+ * here, becase it is reasonable to have only one of these set due to
+ * issues with SSL certificates.
+ *
+ * @param string $path Path to be rewritten
+ * @return string
+ */
+ protected function get_converted_http_link($path) {
+ return preg_replace('/^http:/', 'https:', $this->getExternalTestFileUrl($path, false));
+ }
+
/**
* Test upgrade_http_links
* @param string $content Example content that we'll attempt to replace.
// Get the http url, since the default test wwwroot is https.
$wwwrootdomain = 'www.example.com';
$wwwroothttp = preg_replace('/^https:/', 'http:', $CFG->wwwroot);
- $testdomain = 'download.moodle.org';
+ $testdomain = $this->get_converted_http_link('');
return [
"Test image from an available site so shouldn't be reported" => [
"content" => '<img src="' . $this->getExternalTestFileUrl('/test.jpg', false) . '">',
function xmldb_tool_log_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_logstore_database_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_logstore_standard_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
foreach ($records as $record) {
$instances[] = \core\message\inbound\manager::get_handler($record->classname);
}
+ $records->close();
echo $OUTPUT->header();
echo $renderer->messageinbound_handlers_table($instances);
$dbman = $DB->get_manager();
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
if ($oldversion < 2016052305) {
// Define field inactivedate to be added to tool_monitor_subscriptions.
$mform->addRule('clientsecret', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addHelpButton('clientsecret', 'issuerclientsecret', 'tool_oauth2');
+ // Use basic authentication.
+ $mform->addElement('checkbox', 'basicauth', get_string('usebasicauth', 'tool_oauth2'));
+ $mform->addHelpButton('basicauth', 'usebasicauth', 'tool_oauth2');
+
// Login scopes.
$mform->addElement('text', 'loginscopes', get_string('issuerloginscopes', 'tool_oauth2'));
$mform->addRule('loginscopes', null, 'required', null, 'client');
$string['systemaccountconnected'] = 'System account connected';
$string['systemaccountnotconnected'] = 'System account not connected';
$string['systemauthstatus'] = 'System account connected';
+$string['usebasicauth'] = 'Authenticate token requests via HTTP headers';
+$string['usebasicauth_help'] = 'Utilize the HTTP Basic authentication scheme when sending client ID and password with a refresh token request. Recommended by the OAuth 2 standard, but may not be available with some issuers.';
$string['userfieldexternalfield'] = 'External field name';
$string['userfieldexternalfield_help'] = 'Name of the field provided by the external OAuth system.';
$string['userfieldinternalfield_help'] = 'Name of the Moodle user field that should be mapped from the external field.';
$keywordlist = implode(', ', $keywords);
echo $OUTPUT->box(get_string('spamresult', 'tool_spamcleaner').s($keywordlist)).' ...';
- print_user_list(array($spamusers_desc,
- $spamusers_blog,
- $spamusers_blogsub,
- $spamusers_comment,
- $spamusers_message,
- $spamusers_forumpost,
- $spamusers_forumpostsub
- ),
- $keywords);
+ $recordsets = [
+ $spamusers_desc,
+ $spamusers_blog,
+ $spamusers_blogsub,
+ $spamusers_comment,
+ $spamusers_message,
+ $spamusers_forumpost,
+ $spamusers_forumpostsub
+ ];
+ print_user_list($recordsets, $keywords);
+ foreach ($recordsets as $rs) {
+ $rs->close();
+ }
}
// add default values for remaining fields
$formdefaults = array();
- if ($updatetype != UU_UPDATE_FILEOVERRIDE && $updatetype != UU_UPDATE_NOCHANGES) {
+ if (!$existinguser || ($updatetype != UU_UPDATE_FILEOVERRIDE && $updatetype != UU_UPDATE_NOCHANGES)) {
foreach ($STD_FIELDS as $field) {
if (isset($user->$field)) {
continue;
And I set the field "groups" to "Section 1 (1)"
And the "members" select box should contain "Tom Jones"
+ @javascript
+ Scenario: Upload users enrolling them on courses and groups applying defaults
+ Given the following "courses" exist:
+ | fullname | shortname | category |
+ | Maths | math102 | 0 |
+ And the following "groups" exist:
+ | name | course | idnumber |
+ | Section 1 | math102 | S1 |
+ | Section 3 | math102 | S3 |
+ And I log in as "admin"
+ And I navigate to "Upload users" node in "Site administration > Users > Accounts"
+ When I upload "lib/tests/fixtures/upload_users.csv" file to "File" filemanager
+ And I press "Upload users"
+ And I set the following fields to these values:
+ | City/town | Brighton |
+ | Department | Purchasing |
+ And I press "Upload users"
+ And I press "Continue"
+ And I navigate to "Users > Accounts > Browse list of users" in site administration
+ And I should see "Tom Jones"
+ And I follow "Tom Jones"
+ And I follow "Edit profile"
+ And the field "City/town" matches value "Brighton"
+ And the field "Department" matches value "Purchasing"
+
@javascript
Scenario: Upload users with custom profile fields
# Create user profile field.
$users[$key]->country = $countries[$user->country];
}
}
- if ($sort == "country") { // Need to resort by full country name, not code
+ if ($sort == "country") {
+ // Need to resort by full country name, not code.
foreach ($users as $user) {
$susers[$user->id] = $user->country;
}
- asort($susers);
+ // Sort by country name, according to $dir.
+ if ($dir === 'DESC') {
+ arsort($susers);
+ } else {
+ asort($susers);
+ }
foreach ($susers as $key => $value) {
$nusers[] = $users[$key];
}
*
* Lazy load of course data, students and teachers.
*
- * @param int|stdClass $course Course id
+ * @param int|\stdClass $course Course id
* @return void
*/
public function __construct($course) {
*
* Lazy load of course data, students and teachers.
*
- * @param int|stdClass $course Course object or course id
+ * @param int|\stdClass $course Course object or course id
* @return \core_analytics\course
*/
public static function instance($course) {
/**
* Loads the analytics course object.
*
- * @return null
+ * @return void
*/
protected function load() {
/**
* Returns the course students.
*
- * @return stdClass[]
+ * @return int[]
*/
public function get_students() {
*
* Keys are ignored.
*
- * @param int|float $values Sorted array of values
+ * @param int[]|float[] $values Sorted array of values
* @return int
*/
protected function median($values) {
return reset($values);
}
- $middlevalue = floor(($count - 1) / 2);
+ $middlevalue = (int)floor(($count - 1) / 2);
if ($count % 2) {
// Odd number, middle is the median.
}
$existingcalculations[$calculation->indicator][$calculation->sampleid] = $calculation->value;
}
+ $calculations->close();
return $existingcalculations;
}
if (!$this->is_static()) {
$this->model->trained = 0;
}
+ } else if (empty($this->model->timesplitting)) {
+ // A valid timesplitting method needs to be supplied before a model can be enabled.
+ throw new \moodle_exception('invalidtimesplitting', 'analytics', '', $this->model->id);
+
}
// Purge pages with insights as this may change things.
// Training and prediction files are not mixed up.
$trainingfile1 = \core_analytics\dataset_manager::merge_datasets(array($file), $fakemodelid,
'\core\analytics\time_splitting\quarters', \core_analytics\dataset_manager::LABELLED_FILEAREA, false);
+ $this->waitForSecond();
$trainingfile2 = \core_analytics\dataset_manager::merge_datasets(array($file), $fakemodelid,
'\core\analytics\time_splitting\quarters', \core_analytics\dataset_manager::LABELLED_FILEAREA, false);
$this->model->mark_as_trained();
$this->assertEquals($originaluniqueid, $this->model->get_unique_id());
- $this->model->enable();
+ $this->model->enable('\core\analytics\time_splitting\deciles');
$this->assertEquals($originaluniqueid, $this->model->get_unique_id());
// Wait 1 sec so the timestamp changes.
function xmldb_auth_cas_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
$string['auth_dbname_key'] = 'DB name';
$string['auth_dbpass'] = 'Password matching the above username';
$string['auth_dbpass_key'] = 'Password';
-$string['auth_dbpasstype'] = '<p>Specify the format that the password field is using. MD5 hashing is useful for connecting to other common web applications like PostNuke.</p> <p>Use \'internal\' if you want the external database to manage usernames and email addresses, but Moodle to manage passwords. If you use \'internal\', you <i>must</i> provide a populated email address field in the external database, and you must execute both admin/cron.php and auth/db/cli/sync_users.php regularly. Moodle will send an email to new users with a temporary password.</p>';
+$string['auth_dbpasstype'] = '<p>Specify the format that the password field is using.</p> <p>Use \'internal\' if you want the external database to manage usernames and email addresses, but Moodle to manage passwords. If you use \'internal\', you <i>must</i> provide a populated email address field in the external database, and you must execute both admin/cron.php and auth/db/cli/sync_users.php regularly. Moodle will send an email to new users with a temporary password.</p>';
$string['auth_dbpasstype_key'] = 'Password format';
$string['auth_dbreviveduser'] = 'Revived user {$a->name} id {$a->id}';
$string['auth_dbrevivedusererror'] = 'Error reviving user {$a}';
function xmldb_auth_ldap_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
entry in its database. This module can read user attributes from LDAP and prefill
wanted fields in Moodle. For following logins only the username and
password are checked.';
-$string['auth_ldap_expiration_desc'] = 'Select No to disable expired password checking or LDAP to read passwordexpiration time directly from LDAP';
+$string['auth_ldap_expiration_desc'] = 'Select \'{$a->no}\' to disable expired password checking or \'{$a->ldapserver}\' to read the password expiration time directly from the LDAP server';
$string['auth_ldap_expiration_key'] = 'Expiration';
$string['auth_ldap_expiration_warning_desc'] = 'Number of days before password expiration warning is issued.';
$string['auth_ldap_expiration_warning_key'] = 'Expiration warning';
new lang_string('auth_ldap_passwdexpire_settings', 'auth_ldap'), ''));
// Password Expiration.
+
+ // Create the description lang_string object.
+ $strno = get_string('no');
+ $strldapserver = get_string('pluginname', 'auth_ldap');
+ $langobject = new stdClass();
+ $langobject->no = $strno;
+ $langobject->ldapserver = $strldapserver;
+ $description = new lang_string('auth_ldap_expiration_desc', 'auth_ldap', $langobject);
+
+ // Now create the options.
$expiration = array();
- $expiration['0'] = 'no';
- $expiration['1'] = 'LDAP';
+ $expiration['0'] = $strno;
+ $expiration['1'] = $strldapserver;
+
+ // Add the setting.
$settings->add(new admin_setting_configselect('auth_ldap/expiration',
new lang_string('auth_ldap_expiration_key', 'auth_ldap'),
- new lang_string('auth_ldap_expiration_desc', 'auth_ldap'), 0 , $expiration));
+ $description, 0 , $expiration));
// Password Expiration warning.
$settings->add(new admin_setting_configtext('auth_ldap/expiration_warning',
function xmldb_auth_manual_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_auth_mnet_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
if ($oldversion < 2017020700) {
$DB->set_field('course_' . $table . 's', 'availability', $newvalue,
array('id' => $thingid));
}
+ $rs->close();
}
- $rs->close();
}
}
protected $name; // name of the setting
protected $value; // value of the setting
+ protected $unlockedvalue; // Value to set after the setting is unlocked.
protected $vtype; // type of value (setting_base::IS_BOOLEAN/setting_base::IS_INTEGER...)
protected $visibility; // visibility of the setting (setting_base::VISIBLE/setting_base::HIDDEN)
$this->value = $value;
$this->visibility = $visibility;
$this->status = $status;
+ $this->unlockedvalue = $this->value;
// Generate a default ui
$this->uisetting = new base_setting_ui($this);
$this->status = $status;
if ($status !== $oldstatus) { // Status has changed, let's inform dependencies
$this->inform_dependencies(self::CHANGED_STATUS, $oldstatus);
+
+ if ($status == base_setting::NOT_LOCKED) {
+ // When setting gets unlocked set it to the original value.
+ $this->set_value($this->unlockedvalue);
+ }
}
}
*/
abstract public function get_moodleform_properties();
/**
- * Returns true if the dependent setting is locked.
+ * Returns true if the dependent setting is locked by this setting_dependency.
* @return bool
*/
abstract public function is_locked();
$this->value = ($value)?(string)$value:0;
}
/**
- * Returns true if the dependent setting is locked.
+ * Returns true if the dependent setting is locked by this setting_dependency.
* @return bool
*/
public function is_locked() {
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || $this->setting->get_value() == $this->value) {
return true;
}
- // Else return based upon the dependent settings status
- return ($this->dependentsetting->get_status() !== base_setting::NOT_LOCKED);
+ // Else the dependent setting is not locked by this setting_dependency.
+ return false;
}
/**
* Processes a value change in the primary setting
$this->value = $value;
}
/**
- * Returns true if the dependent setting is locked.
+ * Returns true if the dependent setting is locked by this setting_dependency.
* @return bool
*/
public function is_locked() {
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || in_array($this->setting->get_value(), $this->value)) {
return true;
}
- // Else return based upon the dependent settings status
- return ($this->dependentsetting->get_status() !== base_setting::NOT_LOCKED);
+ // Else the dependent setting is not locked by this setting_dependency.
+ return false;
}
/**
* Processes a value change in the primary setting
}
/**
- * Returns true if the dependent setting is locked.
+ * Returns true if the dependent setting is locked by this setting_dependency.
* @return bool
*/
public function is_locked() {
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || !empty($value)) {
return true;
}
- // Else return based upon the dependent settings status
- return ($this->dependentsetting->get_status() !== base_setting::NOT_LOCKED);
+ // Else the dependent setting is not locked by this setting_dependency.
+ return false;
}
}
return ($prevalue != $this->dependentsetting->get_value());
}
/**
- * Returns true if the dependent setting is locked.
+ * Returns true if the dependent setting is locked by this setting_dependency.
* @return bool
*/
public function is_locked() {
if ($this->setting->get_status() !== base_setting::NOT_LOCKED || empty($value)) {
return true;
}
- // Else return based upon the dependent settings status
- return ($this->dependentsetting->get_status() !== base_setting::NOT_LOCKED);
+ // Else the dependent setting is not locked by this setting_dependency.
+ return false;
}
}
* 2. The setting is locked but only by settings that are of the same level (same page)
*
* Condition 2 is really why we have this function
- *
+ * @param int $level Optional, if provided only depedency_settings below or equal to this level are considered,
+ * when checking if the ui_setting is changeable. Although dependencies might cause a lock on this setting,
+ * they could be changeable in the same view.
* @return bool
*/
- public function is_changeable() {
+ public function is_changeable($level = null) {
if ($this->setting->get_status() === backup_setting::NOT_LOCKED) {
// Its not locked so its chanegable.
return true;
return false;
} else if ($this->setting->has_dependencies_on_settings()) {
foreach ($this->setting->get_settings_depended_on() as $dependency) {
+ if ($level && $dependency->get_setting()->get_level() >= $level) {
+ continue;
+ }
if ($dependency->is_locked() && $dependency->get_setting()->get_level() !== $this->setting->get_level()) {
// Its not changeable because one or more dependancies arn't changeable.
return false;
/**
* Returns true if the setting is changeable
+ * @param int $level Optional, if provided only depedency_settings below or equal to this level are considered,
+ * when checking if the ui_setting is changeable. Although dependencies might cause a lock on this setting,
+ * they could be changeable in the same view.
* @return bool
*/
- public function is_changeable() {
+ public function is_changeable($level = null) {
if ($this->changeable === false) {
return false;
} else {
- return parent::is_changeable();
+ return parent::is_changeable($level);
}
}
/**
* Returns true if the setting is changeable, false otherwise
*
+ * @param int $level Optional, if provided only depedency_settings below or equal to this level are considered,
+ * when checking if the ui_setting is changeable. Although dependencies might cause a lock on this setting,
+ * they could be changeable in the same view.
* @return bool
*/
- public function is_changeable() {
+ public function is_changeable($level = null) {
if (count($this->values) == 1) {
return false;
} else {
- return parent::is_changeable();
+ return parent::is_changeable($level);
}
}
public function add_settings(array $settingstasks) {
global $OUTPUT;
+ // Determine highest setting level, which is displayed in this stage. This is relevant for considering only
+ // locks of dependency settings for parent settings, which are not displayed in this stage.
+ $highestlevel = backup_setting::ACTIVITY_LEVEL;
+ foreach ($settingstasks as $st) {
+ list($setting, $task) = $st;
+ if ($setting->get_level() < $highestlevel) {
+ $highestlevel = $setting->get_level();
+ }
+ }
+
$defaults = array();
foreach ($settingstasks as $st) {
list($setting, $task) = $st;
// If the setting cant be changed or isn't visible then add it as a fixed setting.
- if (!$setting->get_ui()->is_changeable() || $setting->get_visibility() != backup_setting::VISIBLE) {
+ if (!$setting->get_ui()->is_changeable($highestlevel) ||
+ $setting->get_visibility() != backup_setting::VISIBLE) {
$this->add_fixed_setting($setting, $task);
continue;
}
--- /dev/null
+@core @core_backup
+Feature: Restore Moodle 2 course backups with different user data settings
+ In order to decide upon including user data during backup and restore of courses
+ As a teacher and an admin
+ I need to be able to set and override backup and restore settings
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@example.com |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category |
+ | Course 1 | C1 | 0 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ And the following "activities" exist:
+ | activity | name | intro | course | idnumber |
+ | data | Test database name | n | C1 | data1 |
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ And I add a "Text input" field to "Test database name" database and I fill the form with:
+ | Field name | Test field name |
+ | Field description | Test field description |
+ And I follow "Templates"
+ And I wait until the page is ready
+ And I log out
+ And I log in as "student1"
+ And I am on "Course 1" course homepage
+ And I add an entry to "Test database name" database with:
+ | Test field name | Student entry |
+ And I press "Save and view"
+ And I log out
+ And I log in as "admin"
+ And I backup "Course 1" course using this options:
+ | Initial | Include enrolled users | 1 |
+ | Confirmation | Filename | test_backup.mbz |
+
+ @javascript
+ Scenario: Restore a backup with user data
+ # "User data" marks the user data field for the section
+ # "-" marks the user data field for the data activity
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 1 |
+ | Schema | User data | 1 |
+ | Schema | - | 1 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should see "Student entry"
+
+ @javascript
+ Scenario: Restore a backup without user data for data activity
+ # "User data" marks the user data field for the section
+ # "-" marks the user data field for the data activity
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 1 |
+ | Schema | User data | 1 |
+ | Schema | - | 0 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should not see "Student entry"
+
+ @javascript
+ Scenario: Restore a backup without user data for section and data activity
+ # "User data" marks the user data field for the section
+ # "-" marks the user data field for the data activity
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 1 |
+ | Schema | User data | 0 |
+ | Schema | - | 0 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should not see "Student entry"
+
+ @javascript
+ Scenario: Restore a backup without user data for section
+ # "User data" marks the user data field for the section
+ # "-" marks the user data field for the data activity
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 1 |
+ | Schema | - | 1 |
+ | Schema | User data | 0 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should not see "Student entry"
+
+ @javascript
+ Scenario: Restore a backup with user data with local config for including users set to 0
+ And I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 0 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should not see "Student entry"
+
+ @javascript
+ Scenario: Restore a backup with user data with site config for including users set to 0
+ Given I navigate to "General restore defaults" node in "Site administration > Courses > Backups"
+ And I set the field "s_restore_restore_general_users" to ""
+ And I press "Save changes"
+ And I am on "Course 1" course homepage
+ And I navigate to "Restore" node in "Course administration"
+ # "User data" marks the user data field for the section
+ # "-" marks the user data field for the data activity
+ And I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 1 |
+ | Schema | User data | 1 |
+ | Schema | - | 1 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should see "Student entry"
+
+ @javascript
+ Scenario: Restore a backup with user data with local and site config config for including users set to 0
+ Given I navigate to "General restore defaults" node in "Site administration > Courses > Backups"
+ And I set the field "s_restore_restore_general_users" to ""
+ And I press "Save changes"
+ And I am on "Course 1" course homepage
+ And I navigate to "Restore" node in "Course administration"
+ When I restore "test_backup.mbz" backup into a new course using this options:
+ | Settings | Include enrolled users | 0 |
+ Then I should see "Test database name"
+ When I follow "Test database name"
+ Then I should not see "Student entry"
\ No newline at end of file
if (!empty($badge->recipient->id)) {
if ($bake && ($badge->recipient->id == $USER->id)) {
$name = str_replace(' ', '_', $badge->badgeclass['name']) . '.png';
+ $name = clean_param($name, PARAM_FILE);
$filehash = badges_bake($id, $badge->badgeid, $USER->id, true);
$fs = get_file_storage();
$file = $fs->get_file_by_hash($filehash);
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'Badge id.', VALUE_OPTIONAL),
- 'name' => new external_value(PARAM_FILE, 'Badge name.'),
+ 'name' => new external_value(PARAM_TEXT, 'Badge name.'),
'description' => new external_value(PARAM_NOTAGS, 'Badge description.'),
'badgeurl' => new external_value(PARAM_URL, 'Badge URL.'),
'timecreated' => new external_value(PARAM_INT, 'Time created.', VALUE_OPTIONAL),
}
}
+ /**
+ * Triggered when 'badge_awarded' event happens.
+ *
+ * @param \core\event\badge_awarded $event event generated when a badge is awarded.
+ */
+ public static function badge_criteria_review(\core\event\badge_awarded $event) {
+ global $DB, $CFG;
+
+ if (!empty($CFG->enablebadges)) {
+ require_once($CFG->dirroot.'/lib/badgeslib.php');
+ $userid = $event->relateduserid;
+
+ if ($rs = $DB->get_records('badge_criteria', array('criteriatype' => BADGE_CRITERIA_TYPE_BADGE))) {
+ foreach ($rs as $r) {
+ $badge = new badge($r->badgeid);
+ if (!$badge->is_active() || $badge->is_issued($userid)) {
+ continue;
+ }
+
+ if ($badge->criteria[BADGE_CRITERIA_TYPE_BADGE]->review($userid)) {
+ $badge->criteria[BADGE_CRITERIA_TYPE_BADGE]->mark_complete($userid);
+
+ if ($badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->review($userid)) {
+ $badge->criteria[BADGE_CRITERIA_TYPE_OVERALL]->mark_complete($userid);
+ $badge->issue($userid);
+ }
+ }
+ }
+ }
+ }
+ }
/**
* Triggered when 'user_updated' event happens.
*
*/
define('BADGE_CRITERIA_TYPE_PROFILE', 6);
+/*
+ * Badge completion criteria type
+ * Criteria type constant, primarily for storing criteria type in the database.
+ */
+define('BADGE_CRITERIA_TYPE_BADGE', 7);
+
/*
* Criteria type constant to class name mapping
*/
BADGE_CRITERIA_TYPE_SOCIAL => 'social',
BADGE_CRITERIA_TYPE_COURSE => 'course',
BADGE_CRITERIA_TYPE_COURSESET => 'courseset',
- BADGE_CRITERIA_TYPE_PROFILE => 'profile'
+ BADGE_CRITERIA_TYPE_PROFILE => 'profile',
+ BADGE_CRITERIA_TYPE_BADGE => 'badge',
);
/**
--- /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 file contains the badge earned badge award criteria type class
+ *
+ * @package core
+ * @subpackage badges
+ * @copyright 2017 Stephen Bourget
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Badge award criteria -- award on badge completion
+ *
+ * @package core
+ * @subpackage badges
+ * @copyright 2017 Stephen Bourget
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class award_criteria_badge extends award_criteria {
+
+ /* @var int Criteria [BADGE_CRITERIA_TYPE_BADGE] */
+ public $criteriatype = BADGE_CRITERIA_TYPE_BADGE;
+
+ public $required_param = 'badge';
+ public $optional_params = array();
+
+ /**
+ * Get criteria details for displaying to users
+ * @param string $short Print short version of criteria
+ * @return string
+ */
+ public function get_details($short = '') {
+ global $DB, $OUTPUT;
+ $output = array();
+ foreach ($this->params as $p) {
+ $badgename = $DB->get_field('badge', 'name', array('id' => $p['badge']));
+ if (!$badgename) {
+ $str = $OUTPUT->error_text(get_string('error:nosuchbadge', 'badges'));
+ } else {
+ $str = html_writer::tag('b', '"' . $badgename . '"');
+ }
+ $output[] = $str;
+ }
+
+ if ($short) {
+ return implode(', ', $output);
+ } else {
+ return html_writer::alist($output, array(), 'ul');
+ }
+ }
+
+ /**
+ * Add appropriate new criteria options to the form
+ * @param object $mform moodle form
+ */
+ public function get_options(&$mform) {
+ global $DB;
+ $none = false;
+ $availablebadges = null;
+
+ $mform->addElement('header', 'first_header', $this->get_title());
+ $mform->addHelpButton('first_header', 'criteria_' . $this->criteriatype, 'badges');
+
+ // Determine if this badge is a course badge or a site badge.
+ $thisbadge = $DB->get_record('badge', array('id' => $this->badgeid));
+
+ if ($thisbadge->type == BADGE_TYPE_SITE) {
+ // Only list site badges that are enabled.
+ $select = " type = :site AND (status = :status1 OR status = :status2)";
+ $params = array('site' => BADGE_TYPE_SITE,
+ 'status1' => BADGE_STATUS_ACTIVE,
+ 'status2' => BADGE_STATUS_ACTIVE_LOCKED);
+ $availablebadges = $DB->get_records_select_menu('badge', $select, $params, 'name ASC', 'id, name');
+
+ } else if ($thisbadge->type == BADGE_TYPE_COURSE) {
+ // List both site badges and course badges belonging to this course.
+ $select = " (type = :site OR (type = :course AND courseid = :courseid)) AND (status = :status1 OR status = :status2)";
+ $params = array('site' => BADGE_TYPE_SITE,
+ 'course' => BADGE_TYPE_COURSE,
+ 'courseid' => $thisbadge->courseid,
+ 'status1' => BADGE_STATUS_ACTIVE,
+ 'status2' => BADGE_STATUS_ACTIVE_LOCKED);
+ $availablebadges = $DB->get_records_select_menu('badge', $select, $params, 'name ASC', 'id, name');
+ }
+ if (!empty($availablebadges)) {
+ $select = array();
+ $selected = array();
+ foreach ($availablebadges as $bid => $badgename) {
+ if ($bid != $this->badgeid) {
+ // Do not let it use itself as criteria.
+ $select[$bid] = format_string($badgename, true);
+ }
+ }
+
+ if ($this->id !== 0) {
+ $selected = array_keys($this->params);
+ }
+ $settings = array('multiple' => 'multiple', 'size' => 20, 'class' => 'selectbadge');
+ $mform->addElement('select', 'badge_badges', get_string('addbadge', 'badges'), $select, $settings);
+ $mform->addRule('badge_badges', get_string('requiredbadge', 'badges'), 'required');
+ $mform->addHelpButton('badge_badges', 'addbadge', 'badges');
+
+ if ($this->id !== 0) {
+ $mform->setDefault('badge_badges', $selected);
+ }
+ } else {
+ $mform->addElement('static', 'nobadges', '', get_string('error:nobadges', 'badges'));
+ $none = true;
+ }
+
+ // Add aggregation.
+ if (!$none) {
+ $mform->addElement('header', 'aggregation', get_string('method', 'badges'));
+ $agg = array();
+ $agg[] =& $mform->createElement('radio', 'agg', '', get_string('allmethodbadges', 'badges'), 1);
+ $agg[] =& $mform->createElement('radio', 'agg', '', get_string('anymethodbadges', 'badges'), 2);
+ $mform->addGroup($agg, 'methodgr', '', array('<br/>'), false);
+ if ($this->id !== 0) {
+ $mform->setDefault('agg', $this->method);
+ } else {
+ $mform->setDefault('agg', BADGE_CRITERIA_AGGREGATION_ANY);
+ }
+ }
+
+ return array($none, get_string('noparamstoadd', 'badges'));
+ }
+
+ /**
+ * Save criteria records
+ *
+ * @param array $params Values from the form or any other array.
+ */
+ public function save($params = array()) {
+ $badges = $params['badge_badges'];
+ unset($params['badge_badges']);
+ foreach ($badges as $badgeid) {
+ $params["badge_{$badgeid}"] = $badgeid;
+ }
+
+ parent::save($params);
+ }
+
+ /**
+ * Review this criteria and decide if it has been completed
+ *
+ * @param int $userid User whose criteria completion needs to be reviewed.
+ * @param bool $filtered An additional parameter indicating that user list
+ * has been reduced and some expensive checks can be skipped.
+ *
+ * @return bool Whether criteria is complete.
+ */
+ public function review($userid, $filtered = false) {
+
+ global $DB;
+ $overall = false;
+
+ foreach ($this->params as $param) {
+ $badge = $DB->get_record('badge', array('id' => $param['badge']));
+ // See if the user has earned this badge.
+ $awarded = $DB->get_record('badge_issued', array('badgeid' => $param['badge'], 'userid' => $userid));
+
+ // Extra check in case a badge was deleted while this badge is still active.
+ if (!$badge) {
+ if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
+ return false;
+ } else {
+ continue;
+ }
+ }
+
+ if ($this->method == BADGE_CRITERIA_AGGREGATION_ALL) {
+
+ if ($awarded) {
+ $overall = true;
+ continue;
+ } else {
+ return false;
+ }
+ } else if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+ if ($awarded) {
+ return true;
+ } else {
+ $overall = false;
+ continue;
+ }
+ }
+ }
+
+ return $overall;
+ }
+
+ /**
+ * Checks criteria for any major problems.
+ *
+ * @return array A list containing status and an error message (if any).
+ */
+ public function validate() {
+ global $DB;
+ $params = array_keys($this->params);
+ $method = ($this->method == BADGE_CRITERIA_AGGREGATION_ALL);
+ $singleparam = (count($params) == 1);
+
+ foreach ($params as $param) {
+ // Perform check if there only one parameter with any type of aggregation,
+ // Or there are more than one parameter with aggregation ALL.
+
+ if (($singleparam || $method) && !$DB->record_exists('badge', array('id' => $param))) {
+ return array(false, get_string('error:invalidparambadge', 'badges'));
+ }
+ }
+
+ return array(true, '');
+ }
+
+ /**
+ * Returns array with sql code and parameters returning all ids
+ * of users who meet this particular criterion.
+ *
+ * @return array list($join, $where, $params)
+ */
+ public function get_completed_criteria_sql() {
+ $join = '';
+ $where = '';
+ $params = array();
+
+ if ($this->method == BADGE_CRITERIA_AGGREGATION_ANY) {
+ // User has received ANY of the required badges.
+ $join = " LEFT JOIN {badge_issued} bi2 ON bi2.userid = u.id";
+ $where = "AND (";
+ $i = 0;
+ foreach ($this->params as $param) {
+ if ($i == 0) {
+ $where .= ' bi2.badgeid = :badgeid'.$i;
+ } else {
+ $where .= ' OR bi2.badgeid = :badgeid'.$i;
+ }
+ $params['badgeid'.$i] = $param['badge'];
+ $i++;
+ }
+ $where .= ") ";
+ return array($join, $where, $params);
+ } else {
+ // User has received ALL of the required badges.
+ $join = " LEFT JOIN {badge_issued} bi2 ON bi2.userid = u.id";
+ $i = 0;
+ foreach ($this->params as $param) {
+ $i++;
+ $where = ' AND bi2.badgeid = :badgeid'.$i;
+ $params['badgeid'.$i] = $param['badge'];
+ }
+ return array($join, $where, $params);
+ }
+ }
+}
$mform->addElement('header', 'badgedetails', get_string('badgedetails', 'badges'));
$mform->addElement('text', 'name', get_string('name'), array('size' => '70'));
- // Using PARAM_FILE to avoid problems later when downloading badge files.
- $mform->setType('name', PARAM_FILE);
+ // When downloading badge, it will be necessary to clean the name as PARAM_FILE.
+ $mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
require_sesskey();
$badge = new badge($download);
$name = str_replace(' ', '_', $badge->name) . '.png';
+ $name = clean_param($name, PARAM_FILE);
$filehash = badges_bake($hash, $download, $USER->id, true);
$fs = get_file_storage();
$file = $fs->get_file_by_hash($filehash);
$fordb = new stdClass();
$fordb->id = null;
- $fordb->name = "Test badge";
+ $fordb->name = "Test badge with 'apostrophe' and other friends (<>&@#)";
$fordb->description = "Testing badges";
$fordb->timecreated = time();
$fordb->timemodified = time();
Scenario: Add a badge
Given I navigate to "Add a new badge" node in "Site administration > Badges"
And I set the following fields to these values:
- | Name | Test Badge |
+ | Name | Test badge with 'apostrophe' and other friends (<>&@#) |
| Description | Test badge description |
| issuername | Test Badge Site |
| issuercontact | testuser@example.com |
And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
When I press "Create badge"
Then I should see "Edit details"
- And I should see "Test Badge"
+ And I should see "Test badge with 'apostrophe' and other friends (&@#)"
And I should not see "Create badge"
And I follow "Manage badges"
And I should see "Number of badges available: 1"
As an admin
I need to add criteria to badges in the system
+ @javascript
+ Scenario: Award badge on other badges as criteria
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | teacher1 | Teacher | 1 | teacher1@example.com |
+ | student1 | Student | 1 | student1@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category | groupmode |
+ | Course 1 | C1 | 0 | 1 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | teacher1 | C1 | editingteacher |
+ | student1 | C1 | student |
+ And I log in as "teacher1"
+ And I am on "Course 1" course homepage
+ # Create course badge 1.
+ And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I follow "Add a new badge"
+ And I set the following fields to these values:
+ | Name | Course Badge 1 |
+ | Description | Course badge 1 description |
+ | issuername | Tester of course badge |
+ And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
+ And I press "Create badge"
+ And I set the field "type" to "Manual issue by role"
+ And I expand all fieldsets
+ # Set to ANY of the roles awards badge.
+ And I set the field "Teacher" to "1"
+ And I set the field "Any of the selected roles awards the badge" to "1"
+ And I press "Save"
+ And I press "Enable access"
+ And I press "Continue"
+ # Badge #2
+ And I navigate to "Add a new badge" node in "Course administration > Badges"
+ And I follow "Add a new badge"
+ And I set the following fields to these values:
+ | Name | Course Badge 2 |
+ | Description | Course badge 2 description |
+ | issuername | Tester of course badge |
+ And I upload "badges/tests/behat/badge.png" file to "Image" filemanager
+ And I press "Create badge"
+ # Set "course badge 1" as criteria
+ And I set the field "type" to "Awarded badges"
+ And I set the field "id_badge_badges" to "Course Badge 1"
+ And I press "Save"
+ And I press "Enable access"
+ And I press "Continue"
+ And I follow "Manage badges"
+ And I follow "Course Badge 1"
+ And I follow "Recipients (0)"
+ And I press "Award badge"
+ # Award course badge 1 to student 1.
+ And I set the field "potentialrecipients[]" to "Student 1 (student1@example.com)"
+ When I press "Award badge"
+ And I follow "Course Badge 1"
+ And I follow "Recipients (1)"
+ Then I should see "Recipients (1)"
+ And I log out
+ # Student 1 should have both badges.
+ And I log in as "student1"
+ And I follow "Profile" in the user menu
+ When I click on "Course 1" "link" in the "region-main" "region"
+ Then I should see "Course Badge 1"
+ And I should see "Course Badge 2"
+
@javascript
Scenario: Award profile badge
Given I log in as "admin"
function xmldb_block_badges_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_calendar_month_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_community_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_completionstatus_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_course_summary_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
// Input.
$this->content->text .= html_writer::tag('label', get_string('search', 'search'),
array('for' => 'searchform_search', 'class' => 'accesshide'));
- $inputoptions = array('id' => 'searchform_search', 'name' => 'q', 'type' => 'text', 'size' => '15');
+ $inputoptions = array('id' => 'searchform_search', 'name' => 'q', 'class' => 'form-control',
+ 'type' => 'text', 'size' => '15');
$this->content->text .= html_writer::empty_tag('input', $inputoptions);
+ // Context id.
+ if ($this->page->context && $this->page->context->contextlevel !== CONTEXT_SYSTEM) {
+ $this->content->text .= html_writer::empty_tag('input', ['type' => 'hidden',
+ 'name' => 'context', 'value' => $this->page->context->id]);
+ }
+
// Search button.
$this->content->text .= html_writer::tag('button', get_string('search', 'search'),
- array('id' => 'searchform_button', 'type' => 'submit', 'title' => 'globalsearch'));
+ array('id' => 'searchform_button', 'type' => 'submit', 'title' => 'globalsearch', 'class' => 'btn btn-secondary'));
$this->content->text .= html_writer::end_tag('fieldset');
$this->content->text .= html_writer::end_tag('form');
$this->content->text .= html_writer::end_tag('div');
function xmldb_block_html_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
use renderable;
use renderer_base;
use templatable;
+use required_capability_exception;
/**
* Summary renderable class.
$this->user = $user;
// Get the plans.
- $this->plans = api::list_user_plans($this->user->id);
+ try {
+ $this->plans = api::list_user_plans($this->user->id);
+ } catch (required_capability_exception $e) {
+ $this->plans = [];
+ }
// Get the competencies to review.
$this->compstoreview = api::list_user_competencies_to_review(0, 3);
function xmldb_block_navigation_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_quiz_results_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_recent_activity_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_rss_client_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_section_links_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_block_settings_upgrade($oldversion, $block) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
// Calculate which day number is the first, and last day of the week.
$firstdayofweek = $this->firstdayofweek;
- $lastdayofweek = ($firstdayofweek + $daysinweek - 1) % $daysinweek;
// The first week is special as it may have padding at the beginning.
$day = reset($alldays);
$firstdayno = $day['wday'];
- $prepadding = ($firstdayno + $daysinweek - 1) % $daysinweek;
+ $prepadding = ($firstdayno + $daysinweek - $firstdayofweek) % $daysinweek;
$daysinfirstweek = $daysinweek - $prepadding;
$days = array_slice($alldays, 0, $daysinfirstweek);
$week = new week_exporter($this->calendar, $days, $prepadding, ($daysinweek - count($days) - $prepadding), $this->related);
'description' => $event->get_description()->get_value(),
'format' => $event->get_description()->get_format(),
'courseid' => $event->get_course() ? $event->get_course()->get('id') : null,
+ 'categoryid' => $event->get_category() ? $event->get_category()->get('id') : null,
'groupid' => $event->get_group() ? $event->get_group()->get('id') : null,
'userid' => $event->get_user() ? $event->get_user()->get('id') : null,
'repeatid' => $event->get_repeats()->get_id(),
'eventids' => new external_multiple_structure(
new external_value(PARAM_INT, 'event ids')
, 'List of event ids',
- VALUE_DEFAULT, array(), NULL_ALLOWED
- ),
+ VALUE_DEFAULT, array()),
'courseids' => new external_multiple_structure(
new external_value(PARAM_INT, 'course ids')
, 'List of course ids for which events will be returned',
- VALUE_DEFAULT, array(), NULL_ALLOWED
- ),
+ VALUE_DEFAULT, array()),
'groupids' => new external_multiple_structure(
new external_value(PARAM_INT, 'group ids')
, 'List of group ids for which events should be returned',
- VALUE_DEFAULT, array(), NULL_ALLOWED
- ),
+ VALUE_DEFAULT, array()),
'categoryids' => new external_multiple_structure(
new external_value(PARAM_INT, 'Category ids'),
'List of category ids for which events will be returned',
- VALUE_DEFAULT, array()
- ),
+ VALUE_DEFAULT, array()),
), 'Event details', VALUE_DEFAULT, array()),
'options' => new external_single_structure(
array(
}
$categories = array();
- if (empty($params['events']['categoryids']) && !empty($courses)) {
- list($wheresql, $sqlparams) = $DB->get_in_or_equal($courses);
- $wheresql = "id $wheresql";
- $courseswithcategory = $DB->get_records_select('course', $wheresql, $sqlparams);
+ if ($hassystemcap || !empty($courses)) {
- // Grab the list of course categories for the requested course list.
$coursecategories = array();
- foreach ($courseswithcategory as $course) {
- if (empty($course->visible)) {
- if (!has_capability('moodle/course:viewhidden', context_course::instance($course->id))) {
- continue;
+ if (!empty($courses)) {
+ list($wheresql, $sqlparams) = $DB->get_in_or_equal($courses);
+ $wheresql = "id $wheresql";
+ $courseswithcategory = $DB->get_records_select('course', $wheresql, $sqlparams);
+
+ // Grab the list of course categories for the requested course list.
+ foreach ($courseswithcategory as $course) {
+ if (empty($course->visible)) {
+ if (!has_capability('moodle/course:viewhidden', context_course::instance($course->id))) {
+ continue;
+ }
}
+ $category = \coursecat::get($course->category);
+ // Fetch parent categories.
+ $coursecategories = array_merge($coursecategories, [$category->id], $category->get_parents());
}
- $category = \coursecat::get($course->category);
- $coursecategories[] = $category;
}
foreach (\coursecat::get_all() as $category) {
- if (has_capability('moodle/category:manage', $category->get_context(), $USER, false)) {
+ // Skip categories not requested.
+ if (!empty($params['events']['categoryids'])) {
+ if (!in_array($category->id, $params['events']['categoryids'])) {
+ continue;
+ }
+ }
+
+ if (has_capability('moodle/category:manage', $category->get_context())) {
// If a user can manage a category, then they can see all child categories. as well as all parent categories.
$categories[] = $category->id;
}
}
$categories = array_merge($categories, $category->get_parents());
- } else if (isset($coursecategories[$category->id])) {
+ } else if (in_array($category->id, $coursecategories)) {
+
// The user has access to a course in this category.
// Fetch all of the parents too.
$categories = array_merge($categories, [$category->id], $category->get_parents());
$categories[] = $category->id;
}
}
- } else {
- // Build the category list.
- // This includes the current category.
- foreach ($params['events']['categoryids'] as $categoryid) {
- $category = \coursecat::get($categoryid);
- $categories = [$category->id];
- // All of its descendants.
- foreach (\coursecat::get_all() as $cat) {
- if (array_search($categoryid, $cat->get_parents()) !== false) {
- $categories[] = $cat->id;
- }
- }
-
- // And all of its parents.
- $categories = array_merge($categories, $category->get_parents());
- }
}
$funcparam['categories'] = array_unique($categories);
}
$courses = [$course->id => $course];
- $category = (\coursecat::get($course->category))->get_db_record();
+ $category = (\coursecat::get($course->category, MUST_EXIST, true))->get_db_record();
} else if (!empty($categoryid)) {
$course = get_site();
$courses = calendar_get_default_courses();
// A specific course was requested.
// Fetch the category that this course is in, along with all parents.
// Do not include child categories of this category, as the user many not have enrolments in those siblings or children.
- $category = \coursecat::get($course->category);
+ $category = \coursecat::get($course->category, MUST_EXIST, true);
$this->categoryid = $category->id;
$this->categories = $category->get_parents();
if (!empty($course)) {
if (!is_object($course)) {
- $course = $DB->get_record('course', array('id' => $course), '*', MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $course), 'id, groupmode, groupmodeforce', MUST_EXIST);
}
if ($course->id != SITEID) {
$coursecontext = \context_course::instance($course->id);
// This function warms the context cache for the course so the calls
// to load the course context in calendar_get_allowed_types don't result
// in additional DB queries.
- $courses = calendar_get_default_courses(null, '*', true);
+ $courses = calendar_get_default_courses(null, 'id, groupmode, groupmodeforce', true);
// We want to pre-fetch all of the groups for each course in a single
// query to avoid calendar_get_allowed_types from hitting the DB for
$counter++;
if ($element == 'dateselector') {
- $el = $this->mform->addElement('date_selector', 'dateselector' . $counter, null, array('timezone' => 0.0, 'step' => 1));
+ $el = $this->mform->addElement('date_selector',
+ 'dateselector' . $counter, null, array('timezone' => 0.0));
} else {
- $el = $this->mform->addElement('date_time_selector', 'dateselector' . $counter, null, array('timezone' => 0.0, 'step' => 1, 'optional' => false));
+ $el = $this->mform->addElement('date_time_selector',
+ 'dateselector' . $counter, null, array('timezone' => 0.0, 'optional' => false));
}
$submitvalues = array('dateselector' . $counter => $date);
$record->timesort = 0;
$record->type = 1;
$record->courseid = 0;
+ $record->categoryid = 0;
foreach ($properties as $name => $value) {
$record->$name = $value;
$record->timesort = 0;
$record->type = 1;
$record->courseid = 0;
+ $record->categoryid = 0;
foreach ($properties as $name => $value) {
$record->$name = $value;
$record->timesort = 0;
$record->type = 1;
$record->courseid = 0;
+ $record->categoryid = 0;
foreach ($properties as $name => $value) {
$record->$name = $value;
// Create some category events.
$this->setAdminUser();
$record = new stdClass();
+ $record->courseid = 0;
$record->categoryid = $category->id;
- $this->create_calendar_event('category a', $USER->id, 'category', 0, time(), $record);
+ $record->timestart = time() - DAYSECS;
+ $catevent1 = $this->create_calendar_event('category a', $USER->id, 'category', 0, time(), $record);
+ $record = new stdClass();
+ $record->courseid = 0;
$record->categoryid = $category2->id;
- $this->create_calendar_event('category b', $USER->id, 'category', 0, time(), $record);
+ $record->timestart = time() + DAYSECS;
+ $catevent2 = $this->create_calendar_event('category b', $USER->id, 'category', 0, time(), $record);
// Now as student, make sure we get the events of the courses I am enrolled.
$this->setUser($user2);
$paramevents = array('categoryids' => array($category2b->id));
- $options = array('timeend' => time() + 7 * WEEKSECS);
+ $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
// Should be just one, since there's just one category event of the course I am enrolled (course3 - cat2b).
$this->assertEquals(1, count($events['events']));
+ $this->assertEquals($catevent2->id, $events['events'][0]['id']);
+ $this->assertEquals(0, count($events['warnings']));
+
+ // Now get category events but by course (there aren't course events in the course).
+ $paramevents = array('courseids' => array($course3->id));
+ $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
+ $events = core_calendar_external::get_calendar_events($paramevents, $options);
+ $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
+ $this->assertEquals(1, count($events['events']));
+ $this->assertEquals($catevent2->id, $events['events'][0]['id']);
+ $this->assertEquals(0, count($events['warnings']));
+
+ // Empty events in one where I'm not enrolled and one parent category
+ // (parent of a category where this is a course where the user is enrolled).
+ $paramevents = array('categoryids' => array($category2->id, $category->id));
+ $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
+ $events = core_calendar_external::get_calendar_events($paramevents, $options);
+ $events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
+ $this->assertEquals(1, count($events['events']));
+ $this->assertEquals($catevent2->id, $events['events'][0]['id']);
$this->assertEquals(0, count($events['warnings']));
// Admin can see all category events.
$this->setAdminUser();
$paramevents = array('categoryids' => array($category->id, $category2->id, $category2b->id));
- $options = array('timeend' => time() + 7 * WEEKSECS);
+ $options = array('timeend' => time() + 7 * WEEKSECS, 'userevents' => false, 'siteevents' => false);
$events = core_calendar_external::get_calendar_events($paramevents, $options);
$events = external_api::clean_returnvalue(core_calendar_external::get_calendar_events_returns(), $events);
$this->assertEquals(2, count($events['events']));
$this->assertEquals(0, count($events['warnings']));
+ $this->assertEquals($catevent1->id, $events['events'][0]['id']);
+ $this->assertEquals($catevent2->id, $events['events'][1]['id']);
}
/**
$record->timesort = 0;
$record->type = CALENDAR_EVENT_TYPE_STANDARD;
$record->courseid = 0;
+ $record->categoryid = 0;
foreach ($properties as $name => $value) {
$record->$name = $value;
$record->timesort = 0;
$record->type = 1;
$record->courseid = 0;
+ $record->categoryid = 0;
foreach ($properties as $name => $value) {
$record->$name = $value;
echo html_writer::start_tag('div', array('class'=>'heightcontainer'));
echo $OUTPUT->heading(get_string('calendar', 'calendar'));
-if ($view == 'day' || $view == 'upcoming') {
- switch($view) {
- case 'day':
- list($data, $template) = calendar_get_view($calendar, $view);
- echo $renderer->render_from_template($template, $data);
- break;
- case 'upcoming':
- list($data, $template) = calendar_get_view($calendar, $view);
- echo $renderer->render_from_template($template, $data);
- break;
- }
-} else if ($view == 'month') {
- list($data, $template) = calendar_get_view($calendar, $view);
- echo $renderer->render_from_template($template, $data);
-}
+
+list($data, $template) = calendar_get_view($calendar, $view);
+echo $renderer->render_from_template($template, $data);
+
echo html_writer::end_tag('div');
list($data, $template) = calendar_get_footer_options($calendar);
'default' => '',
'null' => NULL_ALLOWED
),
+ 'description' => array(
+ 'type' => PARAM_TEXT,
+ 'default' => '',
+ 'null' => NULL_ALLOWED
+ ),
+ 'descriptionformat' => array(
+ 'type' => PARAM_INT,
+ 'default' => FORMAT_HTML,
+ 'null' => NULL_ALLOWED
+ ),
'visible' => array(
'type' => PARAM_BOOL,
)
$excludedcontexts[] = $ctx->id;
}
}
+ $records->close();
return $excludedcontexts;
}
require_capability('moodle/competency:competencymanage', $competency->get_context());
// Reset the sortorder, use reorder instead.
- $competency->set('sortorder', null);
+ $competency->set('sortorder', 0);
$competency->create();
\core\event\competency_created::create_from_competency($competency)->trigger();
'default' => FORMAT_HTML
),
'sortorder' => array(
- 'default' => null,
+ 'default' => 0,
'type' => PARAM_INT
),
'parentid' => array(
),
'sortorder' => array(
'type' => PARAM_INT,
- 'default' => null,
+ 'default' => 0,
),
);
}
),
'sortorder' => array(
'type' => PARAM_INT,
- 'default' => null,
+ 'default' => 0,
),
);
}
'idnumber' => 'idnumber' . $number,
'description' => 'description' . $number,
'descriptionformat' => FORMAT_HTML,
- 'competencyframeworkid' => $frameworkid,
- 'sortorder' => 0
+ 'competencyframeworkid' => $frameworkid
);
$result = external::create_competency($competency);
return (object) external_api::clean_returnvalue(external::create_competency_returns(), $result);
'shortname' => 'shortname' . $number,
'idnumber' => 'idnumber' . $number,
'description' => 'description' . $number,
- 'descriptionformat' => FORMAT_HTML,
- 'sortorder' => 0
+ 'descriptionformat' => FORMAT_HTML
);
$result = external::update_competency($competency);
return external_api::clean_returnvalue(external::update_competency_returns(), $result);
$completion = new completion_info($course);
$activities = $completion->get_activities();
- $progresses = $completion->get_progress_all();
+ $progresses = $completion->get_progress_all('u.id = :uid', ['uid' => $params['userid']]);
$userprogress = $progresses[$user->id];
$results = array();
$thisprogress = $userprogress->progress[$activity->id];
$state = $thisprogress->completionstate;
$timecompleted = $thisprogress->timemodified;
+ $overrideby = $thisprogress->overrideby;
} else {
$state = COMPLETION_INCOMPLETE;
$timecompleted = 0;
+ $overrideby = null;
}
$results[] = array(
'instance' => $activity->instance,
'state' => $state,
'timecompleted' => $timecompleted,
- 'tracking' => $activity->completion
+ 'tracking' => $activity->completion,
+ 'overrideby' => $overrideby
);
}
'timecompleted' => new external_value(PARAM_INT, 'timestamp for completed activity'),
'tracking' => new external_value(PARAM_INT, 'type of tracking:
0 means none, 1 manual, 2 automatic'),
+ 'overrideby' => new external_value(PARAM_INT, 'The user id who has overriden the status, or null',
+ VALUE_OPTIONAL),
), 'Activity'
), 'List of activities status'
),
$details['requirement'][] = get_string('markingyourselfcomplete', 'completion');
} elseif ($cm->completion == COMPLETION_TRACKING_AUTOMATIC) {
if ($cm->completionview) {
- $details['requirement'][] = get_string('viewingactivity', 'completion', $this->module);
+ $modulename = core_text::strtolower(get_string('modulename', $this->module));
+ $details['requirement'][] = get_string('viewingactivity', 'completion', $modulename);
}
if (!is_null($cm->completiongradeitemnumber)) {
// We added 4 activities, but only 3 with completion enabled and one of those is hidden.
$this->assertCount(3, $result['statuses']);
- // Change teacher role capabilities (disable access al goups).
+ // Override status by teacher.
+ $completion->update_state($cmforum, COMPLETION_INCOMPLETE, $student->id, true);
+
+ $result = core_completion_external::get_activities_completion_status($course->id, $student->id);
+ // We need to execute the return values cleaning process to simulate the web service server.
+ $result = external_api::clean_returnvalue(
+ core_completion_external::get_activities_completion_status_returns(), $result);
+
+ // Check forum has been overriden by the teacher.
+ foreach ($result['statuses'] as $status) {
+ if ($status['cmid'] == $forum->cmid) {
+ $this->assertEquals(COMPLETION_INCOMPLETE, $status['state']);
+ $this->assertEquals(COMPLETION_TRACKING_MANUAL, $status['tracking']);
+ $this->assertEquals($teacher->id, $status['overrideby']);
+ break;
+ }
+ }
+
+ // Change teacher role capabilities (disable access all groups).
$context = context_course::instance($course->id);
assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, $context);
accesslib_clear_all_caches_for_unit_testing();
"require-dev": {
"phpunit/phpunit": "6.4.*",
"phpunit/dbUnit": "3.0.*",
- "moodlehq/behat-extension": "3.34.1",
+ "moodlehq/behat-extension": "3.35.0",
"mikey179/vfsStream": "^1.6"
}
}
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "b36746ace2486c033136c855a63f3793",
+ "content-hash": "7cd70172c941fb07f0a2d4173baef5f1",
"packages": [],
"packages-dev": [
{
},
{
"name": "behat/mink-extension",
- "version": "v2.2",
+ "version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/Behat/MinkExtension.git",
- "reference": "5b4bda64ff456104564317e212c823e45cad9d59"
+ "reference": "badc565b7a1d05c4a4bf49c789045bcf7af6c6de"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Behat/MinkExtension/zipball/5b4bda64ff456104564317e212c823e45cad9d59",
- "reference": "5b4bda64ff456104564317e212c823e45cad9d59",
+ "url": "https://api.github.com/repos/Behat/MinkExtension/zipball/badc565b7a1d05c4a4bf49c789045bcf7af6c6de",
+ "reference": "badc565b7a1d05c4a4bf49c789045bcf7af6c6de",
"shasum": ""
},
"require": {
- "behat/behat": "~3.0,>=3.0.5",
- "behat/mink": "~1.5",
+ "behat/behat": "^3.0.5",
+ "behat/mink": "^1.5",
"php": ">=5.3.2",
- "symfony/config": "~2.2|~3.0"
+ "symfony/config": "^2.7|^3.0|^4.0"
},
"require-dev": {
- "behat/mink-goutte-driver": "~1.1",
- "phpspec/phpspec": "~2.0"
+ "behat/mink-goutte-driver": "^1.1",
+ "phpspec/phpspec": "^2.0"
},
"type": "behat-extension",
"extra": {
"test",
"web"
],
- "time": "2016-02-15T07:55:18+00:00"
+ "time": "2017-11-24T19:30:49+00:00"
},
{
"name": "behat/mink-goutte-driver",
},
{
"name": "fabpot/goutte",
- "version": "v3.2.1",
+ "version": "v3.2.2",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfPHP/Goutte.git",
- "reference": "db5c28f4a010b4161d507d5304e28a7ebf211638"
+ "reference": "395f61d7c2e15a813839769553a4de16fa3b3c96"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/db5c28f4a010b4161d507d5304e28a7ebf211638",
- "reference": "db5c28f4a010b4161d507d5304e28a7ebf211638",
+ "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/395f61d7c2e15a813839769553a4de16fa3b3c96",
+ "reference": "395f61d7c2e15a813839769553a4de16fa3b3c96",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0",
"php": ">=5.5.0",
- "symfony/browser-kit": "~2.1|~3.0",
- "symfony/css-selector": "~2.1|~3.0",
- "symfony/dom-crawler": "~2.1|~3.0"
+ "symfony/browser-kit": "~2.1|~3.0|~4.0",
+ "symfony/css-selector": "~2.1|~3.0|~4.0",
+ "symfony/dom-crawler": "~2.1|~3.0|~4.0"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^3.3 || ^4"
},
"type": "application",
"extra": {
"autoload": {
"psr-4": {
"Goutte\\": "Goutte"
- }
+ },
+ "exclude-from-classmap": [
+ "Goutte/Tests"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"keywords": [
"scraper"
],
- "time": "2017-01-03T13:21:43+00:00"
+ "time": "2017-11-19T08:45:40+00:00"
},
{
"name": "guzzlehttp/guzzle",
},
{
"name": "moodlehq/behat-extension",
- "version": "v3.34.1",
+ "version": "v3.35.0",
"source": {
"type": "git",
"url": "https://github.com/moodlehq/moodle-behat-extension.git",
},
{
"name": "phpspec/prophecy",
- "version": "v1.7.2",
+ "version": "1.7.3",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6"
+ "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6",
- "reference": "c9b8c6088acd19d769d4cc0ffa60a9fe34344bd6",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
+ "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf",
"shasum": ""
},
"require": {
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
- "phpunit/phpunit": "^4.8 || ^5.6.5"
+ "phpunit/phpunit": "^4.8.35 || ^5.7"
},
"type": "library",
"extra": {
"spy",
"stub"
],
- "time": "2017-09-04T11:05:03+00:00"
+ "time": "2017-11-24T13:59:53+00:00"
},
{
"name": "phpunit/dbunit",
- "version": "3.0.1",
+ "version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/dbunit.git",
- "reference": "6b9cec80dca8694243aade33bceb425ccafbbd0d"
+ "reference": "403350339b6aca748ee0067d027d85621992e21f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/6b9cec80dca8694243aade33bceb425ccafbbd0d",
- "reference": "6b9cec80dca8694243aade33bceb425ccafbbd0d",
+ "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/403350339b6aca748ee0067d027d85621992e21f",
+ "reference": "403350339b6aca748ee0067d027d85621992e21f",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"php": "^7.0",
"phpunit/phpunit": "^6.0",
- "symfony/yaml": "^3.0"
+ "symfony/yaml": "^3.0 || ^4.0"
},
"type": "library",
"extra": {
"testing",
"xunit"
],
- "time": "2017-10-19T13:21:48+00:00"
+ "time": "2017-11-18T17:40:34+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "5.2.2",
+ "version": "5.2.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "8ed1902a57849e117b5651fc1a5c48110946c06b"
+ "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/8ed1902a57849e117b5651fc1a5c48110946c06b",
- "reference": "8ed1902a57849e117b5651fc1a5c48110946c06b",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d",
+ "reference": "8e1d2397d8adf59a3f12b2878a3aaa66d1ab189d",
"shasum": ""
},
"require": {
"php": "^7.0",
"phpunit/php-file-iterator": "^1.4.2",
"phpunit/php-text-template": "^1.2.1",
- "phpunit/php-token-stream": "^1.4.11 || ^2.0",
+ "phpunit/php-token-stream": "^2.0",
"sebastian/code-unit-reverse-lookup": "^1.0.1",
"sebastian/environment": "^3.0",
"sebastian/version": "^2.0.1",
"testing",
"xunit"
],
- "time": "2017-08-03T12:40:43+00:00"
+ "time": "2017-11-03T13:47:33+00:00"
},
{
"name": "phpunit/php-file-iterator",
- "version": "1.4.2",
+ "version": "1.4.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
+ "reference": "8ebba84e5bd74fc5fdeb916b38749016c7232f93"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
- "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/8ebba84e5bd74fc5fdeb916b38749016c7232f93",
+ "reference": "8ebba84e5bd74fc5fdeb916b38749016c7232f93",
"shasum": ""
},
"require": {
"filesystem",
"iterator"
],
- "time": "2016-10-03T07:40:28+00:00"
+ "time": "2017-11-24T15:00:59+00:00"
},
{
"name": "phpunit/php-text-template",
},
{
"name": "phpunit/phpunit",
- "version": "6.4.3",
+ "version": "6.4.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "06b28548fd2b4a20c3cd6e247dc86331a7d4db13"
+ "reference": "562f7dc75d46510a4ed5d16189ae57fbe45a9932"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/06b28548fd2b4a20c3cd6e247dc86331a7d4db13",
- "reference": "06b28548fd2b4a20c3cd6e247dc86331a7d4db13",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/562f7dc75d46510a4ed5d16189ae57fbe45a9932",
+ "reference": "562f7dc75d46510a4ed5d16189ae57fbe45a9932",
"shasum": ""
},
"require": {
"testing",
"xunit"
],
- "time": "2017-10-16T13:18:59+00:00"
+ "time": "2017-11-08T11:26:09+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
},
{
"name": "sebastian/comparator",
- "version": "2.0.2",
+ "version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "ae068fede81d06e7bb9bb46a367210a3d3e1fe6a"
+ "reference": "1174d9018191e93cb9d719edec01257fc05f8158"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/ae068fede81d06e7bb9bb46a367210a3d3e1fe6a",
- "reference": "ae068fede81d06e7bb9bb46a367210a3d3e1fe6a",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158",
+ "reference": "1174d9018191e93cb9d719edec01257fc05f8158",
"shasum": ""
},
"require": {
"php": "^7.0",
"sebastian/diff": "^2.0",
- "sebastian/exporter": "^3.0"
+ "sebastian/exporter": "^3.1"
},
"require-dev": {
- "phpunit/phpunit": "^6.0"
+ "phpunit/phpunit": "^6.4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "2.1.x-dev"
}
},
"autoload": {
}
],
"description": "Provides the functionality to compare PHP values for equality",
- "homepage": "http://www.github.com/sebastianbergmann/comparator",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
- "time": "2017-08-03T07:14:59+00:00"
+ "time": "2017-11-03T07:16:52+00:00"
},
{
"name": "sebastian/diff",
},
{
"name": "symfony/browser-kit",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
- "reference": "317d5bdf0127f06db7ea294186132b4f5b036839"
+ "reference": "03f957cd24bf939524f07b8b910c89cfcad722a8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/browser-kit/zipball/317d5bdf0127f06db7ea294186132b4f5b036839",
- "reference": "317d5bdf0127f06db7ea294186132b4f5b036839",
+ "url": "https://api.github.com/repos/symfony/browser-kit/zipball/03f957cd24bf939524f07b8b910c89cfcad722a8",
+ "reference": "03f957cd24bf939524f07b8b910c89cfcad722a8",
"shasum": ""
},
"require": {
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-07T14:12:55+00:00"
},
{
"name": "symfony/class-loader",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/class-loader.git",
- "reference": "7572c904b209fa9907c69a6a9a68243c265a4d01"
+ "reference": "df173ac2af96ce202bf8bb5a3fc0bec8a4fdd4d1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/class-loader/zipball/7572c904b209fa9907c69a6a9a68243c265a4d01",
- "reference": "7572c904b209fa9907c69a6a9a68243c265a4d01",
+ "url": "https://api.github.com/repos/symfony/class-loader/zipball/df173ac2af96ce202bf8bb5a3fc0bec8a4fdd4d1",
+ "reference": "df173ac2af96ce202bf8bb5a3fc0bec8a4fdd4d1",
"shasum": ""
},
"require": {
],
"description": "Symfony ClassLoader Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-05T15:47:03+00:00"
},
{
"name": "symfony/config",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "4ab62407bff9cd97c410a7feaef04c375aaa5cfd"
+ "reference": "8d2649077dc54dfbaf521d31f217383d82303c5f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/4ab62407bff9cd97c410a7feaef04c375aaa5cfd",
- "reference": "4ab62407bff9cd97c410a7feaef04c375aaa5cfd",
+ "url": "https://api.github.com/repos/symfony/config/zipball/8d2649077dc54dfbaf521d31f217383d82303c5f",
+ "reference": "8d2649077dc54dfbaf521d31f217383d82303c5f",
"shasum": ""
},
"require": {
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
- "time": "2017-10-04T18:56:58+00:00"
+ "time": "2017-11-07T14:16:22+00:00"
},
{
"name": "symfony/console",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c"
+ "reference": "63cd7960a0a522c3537f6326706d7f3b8de65805"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/116bc56e45a8e5572e51eb43ab58c769a352366c",
- "reference": "116bc56e45a8e5572e51eb43ab58c769a352366c",
+ "url": "https://api.github.com/repos/symfony/console/zipball/63cd7960a0a522c3537f6326706d7f3b8de65805",
+ "reference": "63cd7960a0a522c3537f6326706d7f3b8de65805",
"shasum": ""
},
"require": {
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-16T15:24:32+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "07447650225ca9223bd5c97180fe7c8267f7d332"
+ "reference": "66e6e046032ebdf1f562c26928549f613d428bd1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/07447650225ca9223bd5c97180fe7c8267f7d332",
- "reference": "07447650225ca9223bd5c97180fe7c8267f7d332",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/66e6e046032ebdf1f562c26928549f613d428bd1",
+ "reference": "66e6e046032ebdf1f562c26928549f613d428bd1",
"shasum": ""
},
"require": {
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-05T15:47:03+00:00"
},
{
"name": "symfony/debug",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
- "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd"
+ "reference": "74557880e2846b5c84029faa96b834da37e29810"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/debug/zipball/eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd",
- "reference": "eb95d9ce8f18dcc1b3dfff00cb624c402be78ffd",
+ "url": "https://api.github.com/repos/symfony/debug/zipball/74557880e2846b5c84029faa96b834da37e29810",
+ "reference": "74557880e2846b5c84029faa96b834da37e29810",
"shasum": ""
},
"require": {
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-10T16:38:39+00:00"
},
{
"name": "symfony/dependency-injection",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "8ebad929aee3ca185b05f55d9cc5521670821ad1"
+ "reference": "4e84f5af2c2d51ee3dee72df40b7fc08f49b4ab8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8ebad929aee3ca185b05f55d9cc5521670821ad1",
- "reference": "8ebad929aee3ca185b05f55d9cc5521670821ad1",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/4e84f5af2c2d51ee3dee72df40b7fc08f49b4ab8",
+ "reference": "4e84f5af2c2d51ee3dee72df40b7fc08f49b4ab8",
"shasum": ""
},
"require": {
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
- "time": "2017-10-04T17:15:30+00:00"
+ "time": "2017-11-13T18:10:32+00:00"
},
{
"name": "symfony/dom-crawler",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
- "reference": "40dafd42d5dad7fe5ad4e958413d92a207522ac1"
+ "reference": "cebe3c068867956e012d9135282ba6a05d8a259e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/40dafd42d5dad7fe5ad4e958413d92a207522ac1",
- "reference": "40dafd42d5dad7fe5ad4e958413d92a207522ac1",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/cebe3c068867956e012d9135282ba6a05d8a259e",
+ "reference": "cebe3c068867956e012d9135282ba6a05d8a259e",
"shasum": ""
},
"require": {
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-05T15:47:03+00:00"
},
{
"name": "symfony/event-dispatcher",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423"
+ "reference": "271d8c27c3ec5ecee6e2ac06016232e249d638d9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d7ba037e4b8221956ab1e221c73c9e27e05dd423",
- "reference": "d7ba037e4b8221956ab1e221c73c9e27e05dd423",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/271d8c27c3ec5ecee6e2ac06016232e249d638d9",
+ "reference": "271d8c27c3ec5ecee6e2ac06016232e249d638d9",
"shasum": ""
},
"require": {
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-05T15:47:03+00:00"
},
{
"name": "symfony/filesystem",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
- "reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1"
+ "reference": "77db266766b54db3ee982fe51868328b887ce15c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/90bc45abf02ae6b7deb43895c1052cb0038506f1",
- "reference": "90bc45abf02ae6b7deb43895c1052cb0038506f1",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/77db266766b54db3ee982fe51868328b887ce15c",
+ "reference": "77db266766b54db3ee982fe51868328b887ce15c",
"shasum": ""
},
"require": {
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
- "time": "2017-10-03T13:33:10+00:00"
+ "time": "2017-11-07T14:12:55+00:00"
},
{
"name": "symfony/polyfill-mbstring",
},
{
"name": "symfony/process",
- "version": "v2.8.28",
+ "version": "v2.8.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "26c9fb02bf06bd6b90f661a5bd17e510810d0176"
+ "reference": "d25449e031f600807949aab7cadbf267712f4eee"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/26c9fb02bf06bd6b90f661a5bd17e510810d0176",
- "reference": "26c9fb02bf06bd6b90f661a5bd17e510810d0176",
+ "url": "https://api.github.com/repos/symfony/process/zipball/d25449e031f600807949aab7cadbf267712f4eee",
+ "reference": "d25449e031f600807949aab7cadbf267712f4eee",
"shasum": ""
},
"require": {
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
- "time": "2017-10-01T21:00:16+00:00"
+ "time": "2017-11-05T15:25:56+00:00"
},
{
"name": "symfony/translation",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f"
+ "reference": "373e553477e55cd08f8b86b74db766c75b987fdb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/409bf229cd552bf7e3faa8ab7e3980b07672073f",
- "reference": "409bf229cd552bf7e3faa8ab7e3980b07672073f",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/373e553477e55cd08f8b86b74db766c75b987fdb",
+ "reference": "373e553477e55cd08f8b86b74db766c75b987fdb",
"shasum": ""
},
"require": {
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
- "time": "2017-10-02T06:42:24+00:00"
+ "time": "2017-11-07T14:12:55+00:00"
},
{
"name": "symfony/yaml",
- "version": "v3.3.10",
+ "version": "v3.3.13",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
- "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46"
+ "reference": "0938408c4faa518d95230deabb5f595bf0de31b9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/yaml/zipball/8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46",
- "reference": "8c7bf1e7d5d6b05a690b715729cb4cd0c0a99c46",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/0938408c4faa518d95230deabb5f595bf0de31b9",
+ "reference": "0938408c4faa518d95230deabb5f595bf0de31b9",
"shasum": ""
},
"require": {
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2017-10-05T14:43:42+00:00"
+ "time": "2017-11-10T18:26:04+00:00"
},
{
"name": "theseer/tokenizer",
// can be removed for MySQL (by default it will
// use 'utf8mb4_unicode_ci'. This option should
// be removed for all other databases.
+ // 'fetchbuffersize' => 100000, // On PostgreSQL, this option sets a limit
+ // on the number of rows that are fetched into
+ // memory when doing a large recordset query
+ // (e.g. search indexing). Default is 100000.
+ // Uncomment and set to a value to change it,
+ // or zero to turn off the limit. You need to
+ // set to zero if you are using pg_bouncer in
+ // 'transaction' mode (it is fine in 'session'
+ // mode).
);
$courseinfo['groupmode'] = $course->groupmode;
$courseinfo['groupmodeforce'] = $course->groupmodeforce;
$courseinfo['defaultgroupingid'] = $course->defaultgroupingid;
- $courseinfo['lang'] = $course->lang;
+ $courseinfo['lang'] = clean_param($course->lang, PARAM_LANG);
$courseinfo['timecreated'] = $course->timecreated;
$courseinfo['timemodified'] = $course->timemodified;
- $courseinfo['forcetheme'] = $course->theme;
+ $courseinfo['forcetheme'] = clean_param($course->theme, PARAM_THEME);
$courseinfo['enablecompletion'] = $course->enablecompletion;
$courseinfo['completionnotify'] = $course->completionnotify;
$courseinfo['courseformatoptions'] = array();
$categoryinfo['visible'] = $category->visible;
$categoryinfo['visibleold'] = $category->visibleold;
$categoryinfo['timemodified'] = $category->timemodified;
- $categoryinfo['theme'] = $category->theme;
+ $categoryinfo['theme'] = clean_param($category->theme, PARAM_THEME);
}
$categoriesinfo[] = $categoryinfo;
foreach ($coursefields as $field) {
$coursesdata[$course->id][$field] = $course->{$field};
}
+
+ // Clean lang and auth fields for external functions (it may content uninstalled themes or language packs).
+ if (isset($coursesdata[$course->id]['theme'])) {
+ $coursesdata[$course->id]['theme'] = clean_param($coursesdata[$course->id]['theme'], PARAM_THEME);
+ }
+ if (isset($coursesdata[$course->id]['lang'])) {
+ $coursesdata[$course->id]['lang'] = clean_param($coursesdata[$course->id]['lang'], PARAM_LANG);
+ }
}
return array(
$this->assertCount(0, $result['courses']);
}
+ /**
+ * Test get_courses_by_field_invalid_theme_and_lang
+ */
+ public function test_get_courses_by_field_invalid_theme_and_lang() {
+ $this->resetAfterTest(true);
+ $this->setAdminUser();
+
+ $course = self::getDataGenerator()->create_course(array('theme' => 'kkt', 'lang' => 'kkl'));
+ $result = core_course_external::get_courses_by_field('id', $course->id);
+ $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result);
+ $this->assertEmpty($result['courses']['0']['theme']);
+ $this->assertEmpty($result['courses']['0']['lang']);
+ }
+
+
public function test_check_updates() {
global $DB;
$this->resetAfterTest(true);
function xmldb_enrol_database_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
'summaryformat' => $course->summaryformat,
'format' => $course->format,
'showgrades' => $course->showgrades,
- 'lang' => $course->lang,
+ 'lang' => clean_param($course->lang, PARAM_LANG),
'enablecompletion' => $course->enablecompletion,
'category' => $course->category,
'progress' => $progress,
$params['perpage']);
$results = array();
- $requiredfields = ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'];
+ // Add also extra user fields.
+ $requiredfields = array_merge(
+ ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
+ get_extra_user_fields($context)
+ );
foreach ($users['users'] as $id => $user) {
// Note: We pass the course here to validate that the current user can at least view user details in this course.
// The user we are looking at is not in this course yet though - but we only fetch the minimal set of
function xmldb_enrol_flatfile_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_enrol_guest_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_enrol_imsenterprise_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
$this->set_xml_file(array($imsuser));
$this->imsplugin->cron();
- $this->assertEquals(1, $DB->get_field('user', 'deleted', array('id' => $user->id), '*', MUST_EXIST));
+ $this->assertEquals(1, $DB->get_field('user', 'deleted', array('id' => $user->id), MUST_EXIST));
}
/**
$this->set_xml_file(array($imsuser));
$this->imsplugin->cron();
- $this->assertEquals(0, $DB->get_field('user', 'deleted', array('id' => $user->id), '*', MUST_EXIST));
+ $this->assertEquals(0, $DB->get_field('user', 'deleted', array('id' => $user->id), MUST_EXIST));
}
/**
function xmldb_enrol_manual_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_enrol_mnet_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_enrol_paypal_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_enrol_self_upgrade($oldversion) {
global $CFG, $DB;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
if ($oldversion < 2016052301) {
// Get roles with manager archetype.
$managerroles = get_archetype_roles('manager');
'enddate' => $timenow + WEEKSECS
);
+ $coursedata2 = array(
+ 'lang' => 'kk', // Check invalid language pack.
+ );
+
$course1 = self::getDataGenerator()->create_course($coursedata1);
- $course2 = self::getDataGenerator()->create_course();
+ $course2 = self::getDataGenerator()->create_course($coursedata2);
$courses = array($course1, $course2);
// Enrol $USER in the courses.
foreach ($coursedata1 as $fieldname => $value) {
$this->assertEquals($courseenrol[$fieldname], $course1->$fieldname);
}
+ } else {
+ // Check language pack. Should be empty since an incorrect one was used when creating the course.
+ $this->assertEmpty($courseenrol['lang']);
}
}
}
if ($status === conversion::STATUS_PENDING || $status === conversion::STATUS_FAILED) {
// The current status is either pending or failed.
// Attempt to pick up a new converter and convert the document.
- $from = \core_filetypes::get_file_extension($file->get_mimetype());
+ $from = pathinfo($file->get_filename(), PATHINFO_EXTENSION);
$converters = $this->get_document_converter_classes($from, $format);
$currentconverter = $this->get_next_converter($converters, $conversion->get('converter'));
return false;
}
- $from = \core_filetypes::get_file_extension($file->get_mimetype());
+ $from = pathinfo($file->get_filename(), PATHINFO_EXTENSION);
if (!$from) {
- // No mime type could be found. Unable to determine converter.
+ // No file extension could be found. Unable to determine converter.
return false;
}
}
/**
- * Test the can_convert_storedfile_to function with a file with indistinguished mimetype.
+ * Test the can_convert_storedfile_to function with a file with a known mimetype and extension.
*/
public function test_can_convert_storedfile_to_docx() {
$returnvalue = (object) [];
$types = \core_filetypes::get_types();
- // A file with filename '.' is a directory.
- $file = $this->get_stored_file('example content', 'example', [
+ $file = $this->get_stored_file('example content', 'example.docx', [
'mimetype' => $types['docx']['type'],
]);
require_once($CFG->dirroot . '/filter/mathjaxloader/db/upgradelib.php');
- if ($oldversion < 2016032200) {
-
- $httpurl = get_config('filter_mathjaxloader', 'httpurl');
- // Don't change the config if it has been manually changed to something besides the default setting value.
- if ($httpurl === "http://cdn.mathjax.org/mathjax/2.5-latest/MathJax.js") {
- set_config('httpurl', 'http://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js', 'filter_mathjaxloader');
- }
-
- $httpsurl = get_config('filter_mathjaxloader', 'httpsurl');
- // Don't change the config if it has been manually changed to something besides the default setting value.
- if ($httpsurl === "https://cdn.mathjax.org/mathjax/2.5-latest/MathJax.js") {
- set_config('httpsurl', 'https://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js', 'filter_mathjaxloader');
- }
-
- upgrade_plugin_savepoint(true, 2016032200, 'filter', 'mathjaxloader');
- }
-
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
if ($oldversion < 2016080200) {
// We are consolodating the two settings for http and https url into only the https
// setting. Since it is preferably to always load the secure resource.
}
upgrade_plugin_savepoint(true, 2016102500, 'filter', 'mathjaxloader');
}
+
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
+ //
if ($oldversion < 2017040300) {
$httpsurl = get_config('filter_mathjaxloader', 'httpsurl');
upgrade_plugin_savepoint(true, 2017040300, 'filter', 'mathjaxloader');
}
+
if ($oldversion < 2017042602) {
$httpsurl = get_config('filter_mathjaxloader', 'httpsurl');
function xmldb_filter_mediaplugin_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_filter_tex_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
function xmldb_gradingform_guide_upgrade($oldversion) {
global $DB;
- if ($oldversion < 2016051100) {
- // Clean up empty string or null marking guide comments.
- $sql = $DB->sql_isempty('gradingform_guide_comments', 'description', true, true);
- $sql .= " OR description IS NULL ";
- $DB->delete_records_select('gradingform_guide_comments', $sql);
- // Main savepoint reached.
- upgrade_plugin_savepoint(true, 2016051100, 'gradingform', 'guide');
- }
-
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
foreach ($records as $record) {
$rv[] = $this->get_instance($record);
}
+ $records->close();
return $rv;
}
function xmldb_gradingform_rubric_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
} else if ($this->get_context()->contextlevel >= CONTEXT_COURSE) {
list($context, $course, $cm) = get_context_info_array($this->get_context()->id);
- if (strval($cm->name) !== '') {
- $title = $cm->name;
+ if ($cm && strval($cm->name) !== '') {
+ $title = format_string($cm->name, true, array('context' => $context));
} else {
debugging('Gradable areas are currently supported at the course module level only', DEBUG_DEVELOPER);
$title = $this->get_component();
while ($user = $gui->next_user()) {
$users[$user->user->id] = $user->user;
}
+ $gui->close();
return $users;
}
function xmldb_gradereport_user_upgrade($oldversion) {
global $CFG;
- // Moodle v3.1.0 release upgrade line.
- // Put any upgrade step following this.
-
// Automatically generated Moodle v3.2.0 release upgrade line.
// Put any upgrade step following this.
/**
* Unit tests for grade/edit/tree/lib.php.
*
- * @pacakge core_grade
+ * @package core_grades
* @category phpunit
* @author Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
/**
* Unit tests for grade/import/lib.php.
*
- * @package core_grade
+ * @package core_grades
* @category phpunit
* @copyright 2015 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
/**
* Unit tests for grade quering
*
- * @pacakge core_grade
+ * @package core_grades
* @category phpunit
* @copyright 2011 Petr Skoda {@link http://skodak.org}
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
/**
* Unit tests for grade/report/user/lib.php.
*
- * @package core_grade
+ * @package core_grades
* @category phpunit
* @copyright 2012 Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
/**
* Unit tests for grade/report/lib.php.
*
- * @pacakge core_grade
+ * @package core_grades
* @category phpunit
* @author Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
/**
* Unit tests for grade/report/user/lib.php.
*
- * @package core_grade
+ * @package core_grades
* @category phpunit
* @copyright 2012 Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
if ($DB->get_record('groups', array('courseid'=>$group->courseid, 'name'=>$group->name))) {
throw new invalid_parameter_exception('Group with the same name already exists in the course');
}
- if (!empty($group->idnumber) && $DB->count_records('groups', array('idnumber' => $group->idnumber))) {
- throw new invalid_parameter_exception('Group with the same idnumber already exists');
- }
// now security checks
$context = context_course::instance($group->courseid, IGNORE_MISSING);
if ($DB->count_records('groupings', array('courseid'=>$grouping->courseid, 'name'=>$grouping->name))) {
throw new invalid_parameter_exception('Grouping with the same name already exists in the course');
}
- if (!empty($grouping->idnumber) && $DB->count_records('groupings', array('idnumber' => $grouping->idnumber))) {
- throw new invalid_parameter_exception('Grouping with the same idnumber already exists');
- }
// Now security checks .
$context = context_course::instance($grouping->courseid);
$DB->count_records('groupings', array('courseid'=>$currentgrouping->courseid, 'name'=>$grouping->name))) {
throw new invalid_parameter_exception('A different grouping with the same name already exists in the course');
}
- // Check if the new modified grouping idnumber already exists.
- if (!empty($grouping->idnumber) && $grouping->idnumber != $currentgrouping->idnumber &&
- $DB->count_records('groupings', array('idnumber' => $grouping->idnumber))) {
- throw new invalid_parameter_exception('A different grouping with the same idnumber already exists');
- }
$grouping->courseid = $currentgrouping->courseid;
//decode encoded commas
$record[$header[$key]] = preg_replace($csv_encode, $csv_delimiter, trim($value));
}
- if ($record[$header[0]]) {
+ if (trim($rawline) !== '') {
// add a new group to the database
// add fields to object $user
}
}
- if (isset($newgroup->idnumber)){
+ if (isset($newgroup->idnumber) && strlen($newgroup->idnumber)) {
//if idnumber is set, we use that.
//unset invalid courseid
if (!$mycourse = $DB->get_record('course', array('idnumber'=>$newgroup->idnumber))) {
echo $OUTPUT->notification(get_string('unknowncourseidnumber', 'error', $newgroup->idnumber));
unset($newgroup->courseid);//unset so 0 doesn't get written to database
+ } else {
+ $newgroup->courseid = $mycourse->id;
}
- $newgroup->courseid = $mycourse->id;
- } else if (isset($newgroup->coursename)){
+ } else if (isset($newgroup->coursename) && strlen($newgroup->coursename)) {
//else use course short name to look up
//unset invalid coursename (if no id)
- if (!$mycourse = $DB->get_record('course', array('shortname', $newgroup->coursename))) {
+ if (!$mycourse = $DB->get_record('course', array('shortname' => $newgroup->coursename))) {
echo $OUTPUT->notification(get_string('unknowncourse', 'error', $newgroup->coursename));
unset($newgroup->courseid);//unset so 0 doesn't get written to database
+ } else {
+ $newgroup->courseid = $mycourse->id;
}
- $newgroup->courseid = $mycourse->id;
} else {