Compare commits
317 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22bce961b7 | |||
| ec6731b117 | |||
| c99581ca26 | |||
| 32ae0dd852 | |||
| 3d7db9e878 | |||
| e6b9178eaa | |||
| 5c7be99010 | |||
| 77952f9b6f | |||
| 2fa0109fbf | |||
| f3e4a11dce | |||
| 038bc7f900 | |||
| b7abe12fb7 | |||
| 5e06871c05 | |||
| 365a403c25 | |||
| 22baec7429 | |||
| bcfe44feb5 | |||
| 8526700385 | |||
| f47f92c0b1 | |||
| a43556a44f | |||
| 3417cf93e0 | |||
| 64baebd086 | |||
| 45a0cd6120 | |||
| c8e7e1c3c5 | |||
| 6493f30813 | |||
| 49ecb5e880 | |||
| 3417222e0a | |||
| 0d982a293c | |||
| ad51194713 | |||
| d2d3a57a6b | |||
| 7b567a1fea | |||
| 66fe9b5b5f | |||
| 3c171cae05 | |||
| b805234673 | |||
| 047785fbfd | |||
| 2665ad8aea | |||
| ff78163dbd | |||
| 70de3574bb | |||
| 125d60d769 | |||
| 3ae3f0e2ee | |||
| e6b5450a2b | |||
| f1e8521765 | |||
| e60ad856d8 | |||
| 9446ec2399 | |||
| c191acfdd6 | |||
| ffb5996060 | |||
| b09c936c74 | |||
| a68cbb2812 | |||
| 069853b01b | |||
| f90fb8390b | |||
| 46343f8cc2 | |||
| bcedb11d99 | |||
| 6afd56af46 | |||
| 00d6486f73 | |||
| f33e0d5222 | |||
| 7488f542d3 | |||
| 7c202963f7 | |||
| 17e1682c4e | |||
| ec8f2eb07d | |||
| d634a259a2 | |||
| 2a4c1c7128 | |||
| da5d103964 | |||
| 73b9bccd80 | |||
| edb84bc356 | |||
| f649aafd98 | |||
| 0afb1ecbec | |||
| 9d37a6f117 | |||
| f74fe038a9 | |||
| 3572b50b47 | |||
| c6a0e94bd1 | |||
| 355c4b8d92 | |||
| 7bd64b53c5 | |||
| ed73eb0dde | |||
| e8ca0ae166 | |||
| 42bd1cf3be | |||
| ef78406324 | |||
| c957f2c268 | |||
| 2ec1b93b31 | |||
| 20668b9a8a | |||
| 3b59deb429 | |||
| 942d43eeac | |||
| 3031e3de72 | |||
| e789d41efc | |||
| 1f236c90b2 | |||
| 944520fd81 | |||
| 4f65073d33 | |||
| 3557004809 | |||
| 51b8cd794f | |||
| 5992720184 | |||
| c12bfcefdd | |||
| bf3a3c56b1 | |||
| 75638047ae | |||
| 4d415133f8 | |||
| 8b395d32e5 | |||
| 8edb87a37f | |||
| 2b33c0e6d4 | |||
| cdef8f4e51 | |||
| d6b67e26e3 | |||
| 0a12ba0807 | |||
| 18ea0d75bc | |||
| 6188594b52 | |||
| 0afcdafdbc | |||
| 0de940286f | |||
| 23854442d3 | |||
| 46a05ebe6c | |||
| 2168da18b7 | |||
| 8e14926847 | |||
| 32e3f25c95 | |||
| 8375df3227 | |||
| 92b38fa1a8 | |||
| 0203b05f13 | |||
| 82938bb562 | |||
| 92126f2ea8 | |||
| c3ddc8e2fd | |||
| e81b7b7487 | |||
| 1941f9ab63 | |||
| 16ae053264 | |||
| 78ff74e0d3 | |||
| 8b0427a3a9 | |||
| 8c66b48b85 | |||
| dc3a628459 | |||
| 915b7b6914 | |||
| cdd6d0d240 | |||
| d30613b147 | |||
| 729ada251b | |||
| 58235c3774 | |||
| 2ea0d1ffde | |||
| 1938195cac | |||
| 83a6957d2e | |||
| cb00fcc297 | |||
| 4e2d813fb3 | |||
| 651f9e6c6b | |||
| 75d54a7b9b | |||
| e66e807e81 | |||
| 2891fb44b9 | |||
| df3a27d865 | |||
| 92be41bf98 | |||
| 3989a44cfb | |||
| bcca46d630 | |||
| c96be3bbef | |||
| 33336b560e | |||
| 1c6b0718be | |||
| d0abd8f9fc | |||
| a5adcc8ad4 | |||
| c4cae28841 | |||
| 492e044470 | |||
| bc8665f4dd | |||
| b4d4e7885a | |||
| 2b1827a6d1 | |||
| 8304784442 | |||
| 4b8c428047 | |||
| fc4d0e7f8c | |||
| e940603c99 | |||
| 66dc42dc7a | |||
| e76cfde358 | |||
| 29b66bc2eb | |||
| d070c63237 | |||
| e17641e19e | |||
| 35ff3eae2d | |||
| c2e9ef8d1d | |||
| 2cbe558754 | |||
| fe94e2bd14 | |||
| 6475e2cb5e | |||
| f4307218e6 | |||
| 0d18af4621 | |||
| 1a58b9a5d1 | |||
| 6ba05a7368 | |||
| c1a6523782 | |||
| 74ee81e38e | |||
| e27dfe0fbc | |||
| 2d2e3dc0eb | |||
| 2b5948200f | |||
| 37340fb27b | |||
| 3d8e2ef988 | |||
| c078a012de | |||
| ae565b0e4d | |||
| 298c1361e8 | |||
| ad9e004174 | |||
| d250014ef7 | |||
| 747a386ed4 | |||
| d9810c83cb | |||
| 7b4b469720 | |||
| 8db207f90f | |||
| ca3c02b188 | |||
| 0be75ddb99 | |||
| 795c5635e0 | |||
| 91bc1d0983 | |||
| 739d0e33dd | |||
| 0f08e89988 | |||
| 564787c47b | |||
| 3ff2f2d474 | |||
| d83185cfae | |||
| 9802346b16 | |||
| 67a4191380 | |||
| 0e21a039da | |||
| 645c3bdd64 | |||
| be60d9e7f7 | |||
| 96b04bdedc | |||
| a685402bd8 | |||
| 2d2f32f242 | |||
| 6bc6c40f5b | |||
| 598c974d46 | |||
| 1ba83f0d5a | |||
| 2ffd848807 | |||
| b5f9f77e4c | |||
| 52d65d0970 | |||
| f46e46c174 | |||
| 3e2afb9d11 | |||
| 6b40b9182b | |||
| 4a56b4efb5 | |||
| e945953705 | |||
| a813ed0dd6 | |||
| b278d32999 | |||
| accc87283a | |||
| b86bb7b4df | |||
| 7129db08e7 | |||
| 9ae3d44588 | |||
| 176bb9eec3 | |||
| f009ad959a | |||
| 5ef74b0c3d | |||
| d68208baa1 | |||
| 03152cd95c | |||
| ff2a8c5a18 | |||
| d7bee96561 | |||
| 41fb3ea48f | |||
| 1eb29f17e3 | |||
| 7d1e74c3da | |||
| 00479a197e | |||
| 91d9281330 | |||
| 3f2082ef15 | |||
| c953223f50 | |||
| 7135925f9e | |||
| 156c08723d | |||
| 2835d3c50d | |||
| d6fee3b572 | |||
| 76ed567062 | |||
| 70c7be1c93 | |||
| cb511fdfb6 | |||
| 2cb5968d25 | |||
| 2c9e28d2b8 | |||
| 620a05aa6a | |||
| 7969d430e1 | |||
| 421a17f2a9 | |||
| abf2bf0175 | |||
| 0243d08185 | |||
| 06a850c19e | |||
| 1ce18b558b | |||
| 2f0bd18ad9 | |||
| fdf3d9bda5 | |||
| a89500f81f | |||
| 1a3a805c57 | |||
| aecfdf39dd | |||
| 1595d0d44f | |||
| d12b219aae | |||
| 205ede3084 | |||
| 4b2ae7cb3f | |||
| bf9e1794d8 | |||
| 025ce03039 | |||
| cf06189838 | |||
| 43868ab6d4 | |||
| 4a821e2db0 | |||
| e674b4d4a9 | |||
| 4f7ef6fed3 | |||
| 46ca89ea8b | |||
| 54525dd238 | |||
| fde6b9ef06 | |||
| ae8ce02b74 | |||
| cacef68723 | |||
| 843f487c22 | |||
| eb0019a7ac | |||
| 84ecc2a5eb | |||
| 920fe36776 | |||
| aa05704599 | |||
| bbb818a6e1 | |||
| b5c6c217d9 | |||
| 71d5abb422 | |||
| a8f69dbed0 | |||
| 12ede4f87f | |||
| f2fc4bccf4 | |||
| 486a9a4d9b | |||
| 50126511aa | |||
| 766a2d3364 | |||
| e95beb73b9 | |||
| 308bf50e76 | |||
| 2352074dc0 | |||
| 3ee0495c62 | |||
| d2d9e5a864 | |||
| 6c581c9648 | |||
| 9da6b92faf | |||
| 0d27d706c6 | |||
| 2e35af9cc2 | |||
| bd50f5f555 | |||
| edb25d4f25 | |||
| fc24fb7af0 | |||
| bd1e060c8f | |||
| e79467c0d8 | |||
| 822ccfdb31 | |||
| f69b9181c6 | |||
| 9e5ae58959 | |||
| e3db987f32 | |||
| 1298a03670 | |||
| 67d18f8804 | |||
| 5383687f30 | |||
| 9a1d82de52 | |||
| 5a469543b0 | |||
| c92f6d056a | |||
| 1f672ef01b | |||
| 50464573be | |||
| 2f373db92b | |||
| 0272abac15 | |||
| 65bad3744d | |||
| 8c4508ffb5 | |||
| 5188941881 | |||
| f8da314e59 | |||
| 2479e3742b | |||
| 8c49ddd994 | |||
| abb4e64e37 | |||
| 591ff7634d |
+3
-6
@@ -17,7 +17,7 @@ php:
|
||||
- 5.6
|
||||
|
||||
addons:
|
||||
postgresql: "9.3"
|
||||
postgresql: "9.6"
|
||||
packages:
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
@@ -53,12 +53,9 @@ matrix:
|
||||
fast_finish: true
|
||||
|
||||
include:
|
||||
# Run grunt/npm install on lowest supported npm version
|
||||
- php: 7.1
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='4'
|
||||
# Run grunt/npm install on highest version ('node' is an alias for the latest node.js version.)
|
||||
- php: 7.1
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='node'
|
||||
env: DB=none TASK=GRUNT NVM_VERSION='8.9'
|
||||
|
||||
exclude:
|
||||
# MySQL - it's just too slow.
|
||||
@@ -95,7 +92,7 @@ install:
|
||||
sudo service postgresql stop
|
||||
sudo mv /var/lib/postgresql /mnt/ramdisk
|
||||
sudo ln -s /mnt/ramdisk/postgresql /var/lib/postgresql
|
||||
sudo service postgresql start 9.3
|
||||
sudo service postgresql start 9.6
|
||||
fi
|
||||
- >
|
||||
if [ "$TASK" = 'PHPUNIT' ];
|
||||
|
||||
@@ -1754,6 +1754,7 @@
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.6.5" level="required">
|
||||
<RESTRICT function="restrict_php_version_72" message="unsupportedphpversion72" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -1919,6 +1920,7 @@
|
||||
<VENDOR name="oracle" version="10.2" />
|
||||
</DATABASE>
|
||||
<PHP version="5.6.5" level="required">
|
||||
<RESTRICT function="restrict_php_version_72" message="unsupportedphpversion72" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
|
||||
@@ -183,9 +183,14 @@ abstract class core_role_capability_table_with_risks extends core_role_capabilit
|
||||
public function get_risk_icon($type) {
|
||||
global $OUTPUT;
|
||||
|
||||
$text = $OUTPUT->pix_icon('i/' . str_replace('risk', 'risk_', $type), get_string($type . 'short', 'admin'));
|
||||
$alt = get_string("{$type}short", "admin");
|
||||
$title = get_string($type, "admin");
|
||||
|
||||
$text = $OUTPUT->pix_icon('i/' . str_replace('risk', 'risk_', $type), $alt, 'moodle', [
|
||||
'title' => $title,
|
||||
]);
|
||||
$action = new popup_action('click', $this->risksurl, 'docspopup');
|
||||
$riskicon = $OUTPUT->action_link($this->risksurl, $text, $action, array('title'=>get_string($type, 'admin')));
|
||||
$riskicon = $OUTPUT->action_link($this->risksurl, $text, $action);
|
||||
|
||||
return $riskicon;
|
||||
}
|
||||
|
||||
+9
-1
@@ -5,11 +5,19 @@
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
redirect_if_major_upgrade_required();
|
||||
|
||||
$query = trim(optional_param('query', '', PARAM_NOTAGS)); // Search string
|
||||
|
||||
$context = context_system::instance();
|
||||
$PAGE->set_context($context);
|
||||
|
||||
$hassiteconfig = has_capability('moodle/site:config', $context);
|
||||
|
||||
if ($hassiteconfig && moodle_needs_upgrading()) {
|
||||
redirect(new moodle_url('/admin/index.php'));
|
||||
}
|
||||
|
||||
admin_externalpage_setup('search', '', array('query' => $query)); // now hidden page
|
||||
|
||||
$adminroot = admin_get_root(); // need all settings here
|
||||
@@ -55,7 +63,7 @@ if ($errormsg !== '') {
|
||||
|
||||
$showsettingslinks = true;
|
||||
|
||||
if (has_capability('moodle/site:config', $context)) {
|
||||
if ($hassiteconfig) {
|
||||
require_once("admin_settings_search_form.php");
|
||||
$form = new admin_settings_search_form();
|
||||
$form->display();
|
||||
|
||||
@@ -128,7 +128,7 @@ class api {
|
||||
'rememberusername' => $CFG->rememberusername,
|
||||
'authloginviaemail' => $CFG->authloginviaemail,
|
||||
'registerauth' => $CFG->registerauth,
|
||||
'forgottenpasswordurl' => $CFG->forgottenpasswordurl,
|
||||
'forgottenpasswordurl' => clean_param($CFG->forgottenpasswordurl, PARAM_URL), // We may expect a mailto: here.
|
||||
'authinstructions' => $authinstructions,
|
||||
'authnoneenabled' => (int) is_enabled_auth('none'),
|
||||
'enablewebservices' => $CFG->enablewebservices,
|
||||
@@ -152,10 +152,11 @@ class api {
|
||||
$url = new moodle_url("/$CFG->admin/tool/mobile/launch.php");
|
||||
$settings['launchurl'] = $url->out(false);
|
||||
|
||||
if ($logourl = $OUTPUT->get_logo_url()) {
|
||||
// Check that we are receiving a moodle_url object, themes can override get_logo_url and may return incorrect values.
|
||||
if (($logourl = $OUTPUT->get_logo_url()) && $logourl instanceof moodle_url) {
|
||||
$settings['logourl'] = $logourl->out(false);
|
||||
}
|
||||
if ($compactlogourl = $OUTPUT->get_compact_logo_url()) {
|
||||
if (($compactlogourl = $OUTPUT->get_compact_logo_url()) && $compactlogourl instanceof moodle_url) {
|
||||
$settings['compactlogourl'] = $compactlogourl->out(false);
|
||||
}
|
||||
|
||||
@@ -210,12 +211,17 @@ class api {
|
||||
}
|
||||
|
||||
if (empty($section) or $section == 'sitepolicies') {
|
||||
$settings->sitepolicy = $CFG->sitepolicy;
|
||||
$settings->disableuserimages = $CFG->disableuserimages;
|
||||
}
|
||||
|
||||
if (empty($section) or $section == 'gradessettings') {
|
||||
require_once($CFG->dirroot . '/user/lib.php');
|
||||
$settings->mygradesurl = user_mygrades_url()->out(false);
|
||||
$settings->mygradesurl = user_mygrades_url();
|
||||
// The previous function may return moodle_url instances or plain string URLs.
|
||||
if ($settings->mygradesurl instanceof moodle_url) {
|
||||
$settings->mygradesurl = $settings->mygradesurl->out(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($section) or $section == 'mobileapp') {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
namespace tool_mobile;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
$string['androidappid'] = 'Android app\'s unique identifier';
|
||||
$string['androidappid_desc'] = 'This setting may be left as default unless you have a custom Android app.';
|
||||
$string['autologinkeygenerationlockout'] = 'Auto-login key generation is blocked because of too many requests within an hour.';
|
||||
$string['autologinkeygenerationlockout'] = 'Auto-login key generation is blocked. You need to wait 6 minutes between requests.';
|
||||
$string['autologinnotallowedtoadmins'] = 'Auto-login is not allowed for site admins.';
|
||||
$string['cachedef_plugininfo'] = 'This stores the list of plugins with mobile addons';
|
||||
$string['clickheretolaunchtheapp'] = 'Click here if the app does not open automatically.';
|
||||
|
||||
@@ -97,11 +97,13 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
set_config('typeoflogin', api::LOGIN_VIA_BROWSER, 'tool_mobile');
|
||||
set_config('logo', 'mock.png', 'core_admin');
|
||||
set_config('logocompact', 'mock.png', 'core_admin');
|
||||
set_config('forgottenpasswordurl', 'mailto:fake@email.zy'); // Test old hack.
|
||||
|
||||
list($authinstructions, $notusedformat) = external_format_text($authinstructions, FORMAT_MOODLE, $context->id);
|
||||
$expected['registerauth'] = 'email';
|
||||
$expected['authinstructions'] = $authinstructions;
|
||||
$expected['typeoflogin'] = api::LOGIN_VIA_BROWSER;
|
||||
$expected['forgottenpasswordurl'] = ''; // Expect empty when it's not an URL.
|
||||
|
||||
if ($logourl = $OUTPUT->get_logo_url()) {
|
||||
$expected['logourl'] = $logourl->out(false);
|
||||
@@ -123,6 +125,10 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
require_once($CFG->dirroot . '/course/format/lib.php');
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
$mysitepolicy = 'http://mysite.is/policy/';
|
||||
set_config('sitepolicy', $mysitepolicy);
|
||||
|
||||
$result = external::get_config();
|
||||
$result = external_api::clean_returnvalue(external::get_config_returns(), $result);
|
||||
|
||||
@@ -143,6 +149,7 @@ class tool_mobile_external_testcase extends externallib_advanced_testcase {
|
||||
array('name' => 'numsections', 'value' => course_get_format($SITE)->get_last_section_number()),
|
||||
array('name' => 'newsitems', 'value' => $SITE->newsitems),
|
||||
array('name' => 'commentsperpage', 'value' => $CFG->commentsperpage),
|
||||
array('name' => 'sitepolicy', 'value' => $mysitepolicy),
|
||||
array('name' => 'disableuserimages', 'value' => $CFG->disableuserimages),
|
||||
array('name' => 'mygradesurl', 'value' => user_mygrades_url()->out(false)),
|
||||
array('name' => 'tool_mobile_forcelogout', 'value' => 0),
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace tool_templatelibrary;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ class auth_email_external extends external_api {
|
||||
new external_value(PARAM_NOTAGS, 'The order of the name fields')
|
||||
),
|
||||
'passwordpolicy' => new external_value(PARAM_RAW, 'Password policy', VALUE_OPTIONAL),
|
||||
'sitepolicy' => new external_value(PARAM_URL, 'Site policy url', VALUE_OPTIONAL),
|
||||
'sitepolicy' => new external_value(PARAM_RAW, 'Site policy', VALUE_OPTIONAL),
|
||||
'defaultcity' => new external_value(PARAM_NOTAGS, 'Default city', VALUE_OPTIONAL),
|
||||
'country' => new external_value(PARAM_ALPHA, 'Default country', VALUE_OPTIONAL),
|
||||
'profilefields' => new external_multiple_structure(
|
||||
|
||||
@@ -121,7 +121,7 @@ $string['didntgetusersfromldap'] = "Did not get any users from LDAP -- error? --
|
||||
$string['gotcountrecordsfromldap'] = "Got {\$a} records from LDAP\n";
|
||||
$string['ldapnotconfigured'] = 'The LDAP host url is currently not configured';
|
||||
$string['morethanoneuser'] = 'Strange! More than one user record found in ldap. Only using the first one.';
|
||||
$string['needbcmath'] = 'You need the BCMath extension to use grace logins with Active Directory';
|
||||
$string['needbcmath'] = 'You need the BCMath extension to use expired password checking with Active Directory.';
|
||||
$string['needmbstring'] = 'You need the mbstring extension to change passwords in Active Directory';
|
||||
$string['nodnforusername'] = 'Error in user_update_password(). No DN for: {$a->username}';
|
||||
$string['noemail'] = 'Tried to send you an email but failed!';
|
||||
|
||||
@@ -141,6 +141,15 @@ abstract class backup implements checksumable {
|
||||
* Usually same than major release zero version, mainly for informative/historic purposes.
|
||||
*/
|
||||
const RELEASE = '3.3';
|
||||
|
||||
/**
|
||||
* Cipher to be used in backup and restore operations.
|
||||
*/
|
||||
const CIPHER = 'aes-256-cbc';
|
||||
/**
|
||||
* Bytes enforced for key, using the cypher above. Restrictive? Yes, but better than unsafe lengths
|
||||
*/
|
||||
const CIPHERKEYLEN = 32;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -112,7 +112,7 @@ class cc_forum extends entities {
|
||||
|
||||
public function get_topic_data ($instance) {
|
||||
|
||||
$topic_data = '';
|
||||
$topic_data = array();
|
||||
|
||||
$topic_file = $this->get_external_xml($instance['resource_indentifier']);
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class cc_quiz extends entities {
|
||||
$last_question_id = 0;
|
||||
$last_answer_id = 0;
|
||||
|
||||
$instances = '';
|
||||
$instances = array();
|
||||
|
||||
$types = array(MOODLE_TYPE_QUIZ, MOODLE_TYPE_QUESTION_BANK);
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ class cc11_basiclti extends entities11 {
|
||||
|
||||
public function get_basiclti_data($instance) {
|
||||
|
||||
$topic_data = '';
|
||||
$topic_data = array();
|
||||
|
||||
$basiclti_file = $this->get_external_xml($instance['resource_indentifier']);
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ class cc11_forum extends entities11 {
|
||||
|
||||
public function get_topic_data ($instance) {
|
||||
|
||||
$topic_data = '';
|
||||
$topic_data = array();
|
||||
|
||||
$topic_file = $this->get_external_xml($instance['resource_indentifier']);
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class cc11_lti extends entities11 {
|
||||
|
||||
public function get_basiclti_data($instance) {
|
||||
|
||||
$topic_data = '';
|
||||
$topic_data = array();
|
||||
|
||||
$basiclti_file = $this->get_external_xml($instance['resource_indentifier']);
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class cc11_quiz extends entities11 {
|
||||
$last_question_id = 0;
|
||||
$last_answer_id = 0;
|
||||
|
||||
$instances = '';
|
||||
$instances = array();
|
||||
|
||||
$types = array(MOODLE_TYPE_QUIZ, MOODLE_TYPE_QUESTION_BANK);
|
||||
|
||||
|
||||
@@ -97,6 +97,138 @@ class base64_encode_final_element extends backup_final_element {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of {@link backup_final_element} that provides symmetric-key AES-256 encryption of contents.
|
||||
*
|
||||
* This final element transparently encrypts, for secure storage and transport, any content
|
||||
* that shouldn't be shown normally in plain text. Usually, passwords or keys that cannot use
|
||||
* hashing algorithms, although potentially can encrypt any content. All information is encoded
|
||||
* using base64.
|
||||
*
|
||||
* Features:
|
||||
* - requires openssl extension to work. Without it contents are completely omitted.
|
||||
* - automatically creates an appropriate default key for the site and stores it into backup_encryptkey config (bas64 encoded).
|
||||
* - uses a different appropriate init vector for every operation, which is transmited with the encrypted contents.
|
||||
* - all generated data is base64 encoded for safe transmission.
|
||||
* - automatically adds "encrypted" attribute for easier detection.
|
||||
* - implements HMAC for providing integrity.
|
||||
*
|
||||
* @copyright 2017 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class encrypted_final_element extends backup_final_element {
|
||||
|
||||
/** @var string cypher appropiate raw key for backups in the site. Defaults to backup_encryptkey config. */
|
||||
protected $key = null;
|
||||
|
||||
/**
|
||||
* Constructor - instantiates a encrypted_final_element, specifying its basic info.
|
||||
*
|
||||
* Overridden to automatically add the 'encrypted' attribute if missing.
|
||||
*
|
||||
* @param string $name name of the element
|
||||
* @param array $attributes attributes this element will handle (optional, defaults to null)
|
||||
*/
|
||||
public function __construct($name, $attributes = null) {
|
||||
parent::__construct($name, $attributes);
|
||||
if (! $this->get_attribute('encrypted')) {
|
||||
$this->add_attributes('encrypted');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encryption key manually, overriding default backup_encryptkey config.
|
||||
*
|
||||
* @param string $key key to be used for encrypting. Required to be 256-bit key.
|
||||
* Use a safe generation technique. See self::generate_encryption_random_key() below.
|
||||
*/
|
||||
protected function set_key($key) {
|
||||
$bytes = strlen($key); // Get key length in bytes.
|
||||
|
||||
// Only accept keys with the expected (backup::CIPHERKEYLEN) key length. There are a number of hashing,
|
||||
// random generators to achieve this esasily, like the one shown below to create the default
|
||||
// site encryption key and ivs.
|
||||
if ($bytes !== backup::CIPHERKEYLEN) {
|
||||
$info = (object)array('expected' => backup::CIPHERKEYLEN, 'found' => $bytes);
|
||||
throw new base_element_struct_exception('encrypted_final_element incorrect key length', $info);
|
||||
}
|
||||
// Everything went ok, store the key.
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the field.
|
||||
*
|
||||
* This method sets the value of the element, encrypted using the specified key for it,
|
||||
* defaulting to (and generating) backup_encryptkey config. HMAC is used for integrity.
|
||||
*
|
||||
* @param string $value plain-text content the will be stored encrypted and encoded.
|
||||
*/
|
||||
public function set_value($value) {
|
||||
|
||||
// No openssl available, skip this field completely.
|
||||
if (!function_exists('openssl_encrypt')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No hmac available, skip this field completely.
|
||||
if (!function_exists('hash_hmac')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cypher not available, skip this field completely.
|
||||
if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we have a good key, manual or default.
|
||||
if (empty($this->key)) {
|
||||
// The key has not been set manually, look for it at config (base64 encoded there).
|
||||
$enckey = get_config('backup', 'backup_encryptkey');
|
||||
if ($enckey === false) {
|
||||
// Has not been set, calculate and save an appropiate random key automatically.
|
||||
$enckey = base64_encode(self::generate_encryption_random_key(backup::CIPHERKEYLEN));
|
||||
set_config('backup_encryptkey', $enckey, 'backup');
|
||||
}
|
||||
$this->set_key(base64_decode($enckey));
|
||||
}
|
||||
|
||||
// Now we need an iv for this operation.
|
||||
$iv = self::generate_encryption_random_key(openssl_cipher_iv_length(backup::CIPHER));
|
||||
|
||||
// Everything is ready, let's encrypt and prepend the 1-shot iv.
|
||||
$value = $iv . openssl_encrypt($value, backup::CIPHER, $this->key, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
// Calculate the hmac of the value (iv + encrypted) and prepend it.
|
||||
$hmac = hash_hmac('sha256', $value, $this->key, true);
|
||||
$value = $hmac . $value;
|
||||
|
||||
// Ready, set the encoded value.
|
||||
parent::set_value(base64_encode($value));
|
||||
|
||||
// Finally, if the field has an "encrypted" attribute, set it to true.
|
||||
if ($att = $this->get_attribute('encrypted')) {
|
||||
$att->set_value('true');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an appropiate random key to be used for encrypting backup information.
|
||||
*
|
||||
* Normally used as site default encryption key (backup_encryptkey config) and also
|
||||
* for calculating the init vectors.
|
||||
*
|
||||
* Note that until PHP 5.6.12 openssl_random_pseudo_bytes() did NOT
|
||||
* use a "cryptographically strong algorithm" {@link https://bugs.php.net/bug.php?id=70014}
|
||||
* But it's beyond my crypto-knowledge when it's worth finding a *real* better alternative.
|
||||
*
|
||||
* @param int $bytes Number of bytes to determine the key length expected.
|
||||
*/
|
||||
protected static function generate_encryption_random_key($bytes) {
|
||||
return openssl_random_pseudo_bytes($bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of backup_nested_element that provides special handling of files
|
||||
*
|
||||
|
||||
@@ -529,7 +529,7 @@ class backup_enrolments_structure_step extends backup_structure_step {
|
||||
$enrol->annotate_ids('role', 'roleid');
|
||||
|
||||
// Add enrol plugin structure.
|
||||
$this->add_plugin_structure('enrol', $enrol, false);
|
||||
$this->add_plugin_structure('enrol', $enrol, true);
|
||||
|
||||
return $enrolments;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Tests for the handling of encrypted contents in backup and restore.
|
||||
*
|
||||
* @package core_backup
|
||||
* @copyright 2016 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
require_once($CFG->dirroot . '/backup/moodle2/backup_custom_fields.php');
|
||||
|
||||
class core_backup_encrypted_content_testscase extends advanced_testcase {
|
||||
|
||||
public function setUp() {
|
||||
if (!function_exists('openssl_encrypt')) {
|
||||
$this->markTestSkipped('OpenSSL extension is not loaded.');
|
||||
|
||||
} else if (!function_exists('hash_hmac')) {
|
||||
$this->markTestSkipped('Hash extension is not loaded.');
|
||||
|
||||
} else if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) {
|
||||
$this->markTestSkipped('Expected cipher not available: ' . backup::CIPHER);
|
||||
}
|
||||
}
|
||||
|
||||
public function test_encrypted_final_element() {
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
// Some basic verifications.
|
||||
$efe = new encrypted_final_element('test', array('encrypted'));
|
||||
$this->assertInstanceOf('encrypted_final_element', $efe);
|
||||
$this->assertSame('test', $efe->get_name());
|
||||
$atts = $efe->get_attributes();
|
||||
$this->assertCount(1, $atts);
|
||||
$att = reset($atts);
|
||||
$this->assertInstanceOf('backup_attribute', $att);
|
||||
$this->assertSame('encrypted', $att->get_name());
|
||||
|
||||
// Using a manually defined (incorrect length) key.
|
||||
$efe = new encrypted_final_element('test', array('encrypted'));
|
||||
$key = 'this_in_not_correct_32_byte_key';
|
||||
try {
|
||||
set_config('backup_encryptkey', base64_encode($key), 'backup');
|
||||
$efe->set_value('tiny_secret');
|
||||
$this->fail('Expecting base_element_struct_exception exception, none happened');
|
||||
} catch (exception $e) {
|
||||
$this->assertInstanceOf('base_element_struct_exception', $e);
|
||||
$this->assertEquals('encrypted_final_element incorrect key length', $e->errorcode);
|
||||
|
||||
}
|
||||
|
||||
// Using a manually defined (correct length) key.
|
||||
$efe = new encrypted_final_element('test', array('testattr', 'encrypted'));
|
||||
$key = hash('md5', 'Moodle rocks and this is not secure key, who cares, it is a test');
|
||||
set_config('backup_encryptkey', base64_encode($key), 'backup');
|
||||
$this->assertEmpty($efe->get_value());
|
||||
$secret = 'This is a secret message that nobody else will be able to read but me 💩 ';
|
||||
$efe->set_value($secret);
|
||||
$atts = $efe->get_attributes();
|
||||
$this->assertCount(2, $atts);
|
||||
$this->assertArrayHasKey('encrypted', $atts); // We added it explicitly.
|
||||
$this->assertTrue($atts['encrypted']->is_set());
|
||||
$this->assertSame('true', $atts['encrypted']->get_value());
|
||||
$this->assertNotEmpty($efe->get_value());
|
||||
$this->assertTrue($efe->is_set());
|
||||
// Get the crypted content and decrypt it manually.
|
||||
$ctext = $efe->get_value();
|
||||
$hmaclen = 32; // SHA256 is 32 bytes.
|
||||
$ivlen = openssl_cipher_iv_length(backup::CIPHER);
|
||||
list($hmac, $iv, $text) = array_values(unpack("a{$hmaclen}hmac/a{$ivlen}iv/a*text", base64_decode($ctext)));
|
||||
$this->assertSame(hash_hmac('sha256', $iv . $text, $key, true), $hmac);
|
||||
$this->assertSame($secret, openssl_decrypt($text, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
|
||||
|
||||
// Using the default site-generated key.
|
||||
$efe = new encrypted_final_element('test', array('testattr'));
|
||||
$this->assertEmpty($efe->get_value());
|
||||
$secret = 'This is a secret message that nobody else will be able to read but me 💩 ';
|
||||
$efe->set_value($secret);
|
||||
$atts = $efe->get_attributes();
|
||||
$this->assertCount(2, $atts);
|
||||
$this->assertArrayHasKey('encrypted', $atts); // Was added automatcally, we did not specify it.
|
||||
$this->assertTrue($atts['encrypted']->is_set());
|
||||
$this->assertSame('true', $atts['encrypted']->get_value());
|
||||
$this->assertNotEmpty($efe->get_value());
|
||||
$this->assertTrue($efe->is_set());
|
||||
// Get the crypted content and decrypt it manually.
|
||||
$ctext = $efe->get_value();
|
||||
$hmaclen = 32; // SHA256 is 32 bytes.
|
||||
$ivlen = openssl_cipher_iv_length(backup::CIPHER);
|
||||
list($hmac, $iv, $text) = array_values(unpack("a{$hmaclen}hmac/a{$ivlen}iv/a*text", base64_decode($ctext)));
|
||||
$key = base64_decode(get_config('backup', 'backup_encryptkey'));
|
||||
$this->assertSame(hash_hmac('sha256', $iv . $text, $key, true), $hmac);
|
||||
$this->assertSame($secret, openssl_decrypt($text, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv));
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,66 @@ abstract class restore_step extends base_step {
|
||||
// Return the passed value with cached offset applied.
|
||||
return $value + $cache[$this->get_restoreid()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns symmetric-key AES-256 decryption of base64 encoded contents.
|
||||
*
|
||||
* This method is used in restore operations to decrypt contents encrypted with
|
||||
* {@link encrypted_final_element} automatically decoding (base64) and decrypting
|
||||
* contents using the key stored in backup_encryptkey config.
|
||||
*
|
||||
* Requires openssl, cipher availability, and key existence (backup
|
||||
* automatically sets it if missing). Integrity is provided via HMAC.
|
||||
*
|
||||
* @param string $value {@link encrypted_final_element} value to decode and decrypt.
|
||||
* @return string|null decoded and decrypted value or null if the operation can not be performed.
|
||||
*/
|
||||
public function decrypt($value) {
|
||||
|
||||
// No openssl available, skip this field completely.
|
||||
if (!function_exists('openssl_encrypt')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// No hash available, skip this field completely.
|
||||
if (!function_exists('hash_hmac')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Cypher not available, skip this field completely.
|
||||
if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the decrypt key. Skip if missing.
|
||||
$key = get_config('backup', 'backup_encryptkey');
|
||||
if ($key === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// And decode it.
|
||||
$key = base64_decode($key);
|
||||
|
||||
// Arrived here, let's proceed with authentication (provides integrity).
|
||||
$hmaclen = 32; // SHA256 is 32 bytes.
|
||||
$ivlen = openssl_cipher_iv_length(backup::CIPHER);
|
||||
list($hmac, $iv, $text) = array_values(unpack("a{$hmaclen}hmac/a{$ivlen}iv/a*text", base64_decode($value)));
|
||||
|
||||
// Verify HMAC matches expectations, skip if not (integrity failed).
|
||||
if (!hash_equals($hmac, hash_hmac('sha256', $iv . $text, $key, true))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Arrived here, integrity is ok, let's decrypt.
|
||||
$result = openssl_decrypt($text, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
|
||||
|
||||
// For some reason decrypt failed (strange, HMAC check should have deteted it), skip this field completely.
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -91,6 +91,52 @@ class backup_step_testcase extends advanced_testcase {
|
||||
$bc->destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* test restore_step class, decrypt method
|
||||
*/
|
||||
public function test_restore_step_decrypt() {
|
||||
|
||||
$this->resetAfterTest(true);
|
||||
|
||||
if (!function_exists('openssl_encrypt')) {
|
||||
$this->markTestSkipped('OpenSSL extension is not loaded.');
|
||||
|
||||
} else if (!function_exists('hash_hmac')) {
|
||||
$this->markTestSkipped('Hash extension is not loaded.');
|
||||
|
||||
} else if (!in_array(backup::CIPHER, openssl_get_cipher_methods())) {
|
||||
$this->markTestSkipped('Expected cipher not available: ' . backup::CIPHER);
|
||||
}
|
||||
|
||||
$bt = new mock_restore_task_basepath('taskname');
|
||||
$bs = new mock_restore_structure_step('steptest', null, $bt);
|
||||
$this->assertTrue(method_exists($bs, 'decrypt'));
|
||||
|
||||
// Let's prepare a string for being decrypted.
|
||||
$secret = 'This is a secret message that nobody else will be able to read but me 💩 ';
|
||||
$key = hash('md5', 'Moodle rocks and this is not secure key, who cares, it is a test');
|
||||
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(backup::CIPHER));
|
||||
$message = $iv . openssl_encrypt($secret, backup::CIPHER, $key, OPENSSL_RAW_DATA, $iv);
|
||||
$hmac = hash_hmac('sha256', $message, $key, true);
|
||||
$crypt = base64_encode($hmac . $message);
|
||||
|
||||
// Running it without a key configured, returns null.
|
||||
$this->assertNull($bs->decrypt($crypt));
|
||||
|
||||
// Store the key into config.
|
||||
set_config('backup_encryptkey', base64_encode($key), 'backup');
|
||||
|
||||
// Verify decrypt works and returns original.
|
||||
$this->assertSame($secret, $bs->decrypt($crypt));
|
||||
|
||||
// Finally, test the integrity failure detection is working.
|
||||
// (this can be caused by changed hmac, key or message, in
|
||||
// this case we are just forcing it via changed hmac).
|
||||
$hmac = md5($message);
|
||||
$crypt = base64_encode($hmac . $message);
|
||||
$this->assertNull($bs->decrypt($crypt));
|
||||
}
|
||||
|
||||
/**
|
||||
* test backup_structure_step class
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,7 @@ class block_glossary_random extends block_base {
|
||||
if (empty($this->config->title)) {
|
||||
$this->title = get_string('pluginname','block_glossary_random');
|
||||
} else {
|
||||
$this->title = $this->config->title;
|
||||
$this->title = format_string($this->config->title, true, ['context' => $this->context]);
|
||||
}
|
||||
|
||||
if (empty($this->config->glossary)) {
|
||||
|
||||
@@ -37,7 +37,11 @@ class block_html extends block_base {
|
||||
}
|
||||
|
||||
function specialization() {
|
||||
$this->title = isset($this->config->title) ? format_string($this->config->title) : format_string(get_string('newhtmlblock', 'block_html'));
|
||||
if (isset($this->config->title)) {
|
||||
$this->title = $this->title = format_string($this->config->title, true, ['context' => $this->context]);
|
||||
} else {
|
||||
$this->title = get_string('newhtmlblock', 'block_html');
|
||||
}
|
||||
}
|
||||
|
||||
function instance_allow_multiple() {
|
||||
|
||||
@@ -122,12 +122,16 @@ class repeat_event_collection implements event_collection_interface {
|
||||
* @param int $start Start offset.
|
||||
* @return \stdClass[]
|
||||
*/
|
||||
protected function load_event_records($start = 1) {
|
||||
protected function load_event_records($start = 0) {
|
||||
global $DB;
|
||||
while ($records = $DB->get_records(
|
||||
while ($records = $DB->get_records_select(
|
||||
'event',
|
||||
['repeatid' => $this->parentid],
|
||||
'',
|
||||
'id <> :parentid AND repeatid = :repeatid',
|
||||
[
|
||||
'parentid' => $this->parentid,
|
||||
'repeatid' => $this->parentid,
|
||||
],
|
||||
'id ASC',
|
||||
'*',
|
||||
$start,
|
||||
self::DB_QUERY_LIMIT
|
||||
|
||||
@@ -872,7 +872,7 @@ class core_calendar_rrule_manager_testcase extends advanced_testcase {
|
||||
$records = $DB->get_records('event', ['repeatid' => $this->event->id], 'timestart ASC', 'id, repeatid, timestart');
|
||||
$expecteddate = new DateTime('first Monday of this month');
|
||||
// Move to the next interval's first Monday if the calculated start date is after this month's first Monday.
|
||||
if ($expecteddate->getTimestamp() < $startdatetime->getTimestamp()) {
|
||||
if ($expecteddate->getTimestamp() < $startdate->getTimestamp()) {
|
||||
$expecteddate->add($interval);
|
||||
$expecteddate->modify('first Monday of this month');
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
class core_cohort_external extends external_api {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
* @since Moodle 2.9
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
require_once("$CFG->dirroot/comment/lib.php");
|
||||
|
||||
|
||||
@@ -1349,7 +1349,8 @@ class api {
|
||||
JOIN {user} u
|
||||
ON u.id = uc.userid
|
||||
WHERE (uc.status = :waitingforreview
|
||||
OR (uc.status = :inreview AND uc.reviewerid = :reviewerid))";
|
||||
OR (uc.status = :inreview AND uc.reviewerid = :reviewerid))
|
||||
AND u.deleted = 0";
|
||||
$ordersql = " ORDER BY c.shortname ASC";
|
||||
$params = array(
|
||||
'inreview' => user_competency::STATUS_IN_REVIEW,
|
||||
|
||||
@@ -4564,6 +4564,9 @@ class core_competency_api_testcase extends advanced_testcase {
|
||||
}
|
||||
|
||||
public function test_list_user_competencies_to_review() {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/user/lib.php');
|
||||
|
||||
$dg = $this->getDataGenerator();
|
||||
$this->resetAfterTest();
|
||||
$ccg = $dg->get_plugin_generator('core_competency');
|
||||
@@ -4580,10 +4583,12 @@ class core_competency_api_testcase extends advanced_testcase {
|
||||
|
||||
$u1 = $dg->create_user();
|
||||
$u2 = $dg->create_user();
|
||||
$u3 = $dg->create_user();
|
||||
$f1 = $ccg->create_framework();
|
||||
$c1 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
|
||||
$c2 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
|
||||
$c3 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
|
||||
$c4 = $ccg->create_competency(['competencyframeworkid' => $f1->get('id')]);
|
||||
$uc1a = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $c1->get('id'),
|
||||
'status' => user_competency::STATUS_IDLE]);
|
||||
$uc1b = $ccg->create_user_competency(['userid' => $u1->id, 'competencyid' => $c2->get('id'),
|
||||
@@ -4596,14 +4601,23 @@ class core_competency_api_testcase extends advanced_testcase {
|
||||
'status' => user_competency::STATUS_IDLE]);
|
||||
$uc2c = $ccg->create_user_competency(['userid' => $u2->id, 'competencyid' => $c3->get('id'),
|
||||
'status' => user_competency::STATUS_IN_REVIEW]);
|
||||
$uc3a = $ccg->create_user_competency(['userid' => $u3->id, 'competencyid' => $c4->get('id'),
|
||||
'status' => user_competency::STATUS_WAITING_FOR_REVIEW]);
|
||||
|
||||
// The reviewer can review all plans waiting for review, or in review where they are the reviewer.
|
||||
$this->setUser($reviewer);
|
||||
$result = api::list_user_competencies_to_review();
|
||||
$this->assertEquals(3, $result['count']);
|
||||
$this->assertEquals(4, $result['count']);
|
||||
$this->assertEquals($uc2a->get('id'), $result['competencies'][0]->usercompetency->get('id'));
|
||||
$this->assertEquals($uc1b->get('id'), $result['competencies'][1]->usercompetency->get('id'));
|
||||
$this->assertEquals($uc1c->get('id'), $result['competencies'][2]->usercompetency->get('id'));
|
||||
$this->assertEquals($uc3a->get('id'), $result['competencies'][3]->usercompetency->get('id'));
|
||||
|
||||
// Now, let's delete user 3.
|
||||
// It should not be listed on user competencies to review any more.
|
||||
user_delete_user($u3);
|
||||
$result = api::list_user_competencies_to_review();
|
||||
$this->assertEquals(3, $result['count']);
|
||||
|
||||
// The reviewer cannot view the plans when they do not have the permission in the user's context.
|
||||
role_assign($roleprohibit, $reviewer->id, context_user::instance($u2->id)->id);
|
||||
|
||||
@@ -59,7 +59,10 @@ class api {
|
||||
if (is_object($instanceorid)) {
|
||||
$instance = $instanceorid;
|
||||
} else {
|
||||
$instance = $DB->get_record($modulename, array('id' => $instanceorid), '*', MUST_EXIST);
|
||||
$instance = $DB->get_record($modulename, array('id' => $instanceorid), '*', IGNORE_MISSING);
|
||||
}
|
||||
if (!$instance) {
|
||||
return false;
|
||||
}
|
||||
$course = get_course($instance->course);
|
||||
|
||||
|
||||
@@ -72,6 +72,13 @@ class core_completion_api_testcase extends advanced_testcase {
|
||||
$this->assertEquals(\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED, $event->eventtype);
|
||||
$this->assertEquals($time, $event->timestart);
|
||||
$this->assertEquals($time, $event->timesort);
|
||||
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
// Delete the module.
|
||||
course_delete_module($assign->cmid);
|
||||
|
||||
// Check we don't get a failure when called on a deleted module.
|
||||
\core_completion\api::update_completion_date_event($assign->cmid, 'assign', null, $time);
|
||||
}
|
||||
|
||||
public function test_update_completion_date_event_update() {
|
||||
|
||||
@@ -259,6 +259,8 @@ $CFG->admin = 'admin';
|
||||
// $CFG->session_memcached_prefix = 'memc.sess.key.';
|
||||
// $CFG->session_memcached_acquire_lock_timeout = 120;
|
||||
// $CFG->session_memcached_lock_expire = 7200; // Ignored if PECL memcached is below version 2.2.0
|
||||
// $CFG->session_memcached_lock_retry_sleep = 150; // Spin-lock retry sleeptime (msec). Only effective
|
||||
// // for tuning php-memcached 3.0.x (PHP 7)
|
||||
//
|
||||
// Redis session handler (requires redis server and redis extension):
|
||||
// $CFG->session_handler_class = '\core\session\redis';
|
||||
|
||||
@@ -268,8 +268,9 @@ class core_course_external extends external_api {
|
||||
|
||||
if (!empty($cm->showdescription) or $cm->modname == 'label') {
|
||||
// We want to use the external format. However from reading get_formatted_content(), $cm->content format is always FORMAT_HTML.
|
||||
$options = array('noclean' => true);
|
||||
list($module['description'], $descriptionformat) = external_format_text($cm->content,
|
||||
FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id);
|
||||
FORMAT_HTML, $modcontext->id, $cm->modname, 'intro', $cm->id, $options);
|
||||
}
|
||||
|
||||
//url of the module
|
||||
@@ -3135,6 +3136,7 @@ class core_course_external extends external_api {
|
||||
*/
|
||||
public static function check_updates($courseid, $tocheck, $filter = array()) {
|
||||
global $CFG, $DB;
|
||||
require_once($CFG->dirroot . "/course/lib.php");
|
||||
|
||||
$params = self::validate_parameters(
|
||||
self::check_updates_parameters(),
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.course-content ul.topics li.section .right img.icon {
|
||||
padding: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.course-content ul.topics li.section .left {
|
||||
padding-top: 22px;
|
||||
text-align: right;
|
||||
|
||||
@@ -13,10 +13,6 @@
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.course-content ul.weeks li.section .right img.icon {
|
||||
padding: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.course-content ul.weeks li.section .left {
|
||||
padding-top: 22px;
|
||||
text-align: right;
|
||||
|
||||
+6
-3
@@ -1403,7 +1403,9 @@ function course_module_update_calendar_events($modulename, $instance = null, $cm
|
||||
if (!isset($cm)) {
|
||||
$cm = get_coursemodule_from_instance($modulename, $instance->id, $instance->course);
|
||||
}
|
||||
course_module_calendar_event_update_process($instance, $cm);
|
||||
if (!empty($cm)) {
|
||||
course_module_calendar_event_update_process($instance, $cm);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1432,8 +1434,9 @@ function course_module_bulk_update_calendar_events($modulename, $courseid = 0) {
|
||||
}
|
||||
|
||||
foreach ($instances as $instance) {
|
||||
$cm = get_coursemodule_from_instance($modulename, $instance->id, $instance->course);
|
||||
course_module_calendar_event_update_process($instance, $cm);
|
||||
if ($cm = get_coursemodule_from_instance($modulename, $instance->id, $instance->course)) {
|
||||
course_module_calendar_event_update_process($instance, $cm);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ class converter implements \core_files\converter_interface {
|
||||
'xls' => 'application/vnd.google-apps.spreadsheet',
|
||||
'xlsx' => 'application/vnd.google-apps.spreadsheet',
|
||||
'ppt' => 'application/vnd.google-apps.presentation',
|
||||
'pptx' => 'application/vnd.google-apps.presentation'
|
||||
'pptx' => 'application/vnd.google-apps.presentation',
|
||||
'html' => 'application/vnd.google-apps.document'
|
||||
];
|
||||
|
||||
/** @var array $export List of supported export file formats */
|
||||
@@ -265,6 +266,6 @@ class converter implements \core_files\converter_interface {
|
||||
* @return string
|
||||
*/
|
||||
public function get_supported_conversions() {
|
||||
return implode(', ', ['rtf', 'doc', 'xls', 'docx', 'xlsx', 'ppt', 'pptx', 'pdf']);
|
||||
return implode(', ', ['rtf', 'doc', 'xls', 'docx', 'xlsx', 'ppt', 'pptx', 'pdf', 'html']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
require_once("$CFG->libdir/filelib.php");
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ MathJax.Hub.Config({
|
||||
// setting. Since it is preferably to always load the secure resource.
|
||||
|
||||
$httpurl = get_config('filter_mathjaxloader', 'httpurl');
|
||||
if ($httpurl !== 'http://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js') {
|
||||
if ($httpurl !== 'http://cdn.mathjax.org/mathjax/2.6-latest/MathJax.js' &&
|
||||
$httpurl !== 'http://cdn.mathjax.org/mathjax/2.6.1/MathJax.js') {
|
||||
// If the http setting has been changed, we make the admin choose the https setting because
|
||||
// it indicates some sort of custom setup. This will be supported by the release notes.
|
||||
unset_config('httpsurl', 'filter_mathjaxloader');
|
||||
@@ -200,5 +201,15 @@ MathJax.Hub.Config({
|
||||
// Automatically generated Moodle v3.3.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
if ($oldversion < 2017051501) {
|
||||
|
||||
$httpsurl = get_config('filter_mathjaxloader', 'httpsurl');
|
||||
if (empty($httpsurl)) {
|
||||
// URL is empty, most likely because of bad upgrade path. See MDL-59780.
|
||||
set_config('httpsurl', 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js', 'filter_mathjaxloader');
|
||||
}
|
||||
upgrade_plugin_savepoint(true, 2017051501, 'filter', 'mathjaxloader');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2017051500;
|
||||
$plugin->version = 2017051501;
|
||||
$plugin->requires = 2017050500; // Requires this Moodle version.
|
||||
$plugin->component= 'filter_mathjaxloader';
|
||||
|
||||
@@ -489,7 +489,7 @@ class grading_manager {
|
||||
* Returns the given method's controller in the gradable area
|
||||
*
|
||||
* @param string $method the method name, eg 'rubric' (must be available)
|
||||
* @return grading_controller
|
||||
* @return gradingform_controller
|
||||
*/
|
||||
public function get_controller($method) {
|
||||
global $CFG, $DB;
|
||||
@@ -534,7 +534,7 @@ class grading_manager {
|
||||
/**
|
||||
* Returns the controller for the active method if it is available
|
||||
*
|
||||
* @return null|grading_controller
|
||||
* @return null|gradingform_controller
|
||||
*/
|
||||
public function get_active_controller() {
|
||||
if ($gradingmethod = $this->get_active_method()) {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once("$CFG->libdir/externallib.php");
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,7 +36,7 @@ $string['chooselanguagehead'] = 'Sprache wählen';
|
||||
$string['chooselanguagesub'] = 'Wählen Sie eine Sprache, die Sie während der Installation verwenden wollen. Die ausgewählte Sprache wird nach der Installation als Standardsprache der Instanz benutzt, aber Sie dürfen die Sprache jederzeit ändern.';
|
||||
$string['clialreadyconfigured'] = 'Die Datei config.php existiert bereits. Bitte benutzen Sie admin/cli/install_database.php, wenn Sie diese Site installieren möchten.';
|
||||
$string['clialreadyinstalled'] = 'Die Datei config.php existiert bereits. Bitte benutzen Sie admin/cli/install_database.php, wenn Sie diese Site aktualisieren möchten.';
|
||||
$string['cliinstallheader'] = 'Installation von Moodle {$a} über die Kommandozeile';
|
||||
$string['cliinstallheader'] = 'Installation von Moodle {$a} über die Befehlszeile';
|
||||
$string['databasehost'] = 'Datenbank-Server';
|
||||
$string['databasename'] = 'Datenbank-Name';
|
||||
$string['databasetypehead'] = 'Datenbank-Treiber wählen';
|
||||
|
||||
@@ -88,9 +88,9 @@ $string['pathsunsecuredataroot'] = 'Andmete juurkataloogi asukoht pole turvaline
|
||||
$string['pathswrongadmindir'] = 'Halduskataloogi pole olemas';
|
||||
$string['phpextension'] = '{$a} PHP-laiendus';
|
||||
$string['phpversion'] = 'PHP versioon';
|
||||
$string['phpversionhelp'] = '<p>Moodle vajab vähemalt PHP versiooni 4.1.0</p>
|
||||
<p>Sinu jooksev versioon on {$a}</p>
|
||||
<p>Sa pead oma PHP-d uuendama või kolima hosti, kus on uuem PHP versioon!</p>';
|
||||
$string['phpversionhelp'] = '<p>Moodle vajab vähemalt PHP versiooni 5.6.5 või 7.1 (versioonil 7.0.x on mõned mootoripiirangud).</p>
|
||||
<p>Teie praegune versioon on {$a}.</p>
|
||||
<p>Peate uuendama oma PHP või kolima hosti, kus on uuem PHP versioon.</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Teile kuvatakse see leht, kuna olete edukalt oma arvutisse installinud ja
|
||||
käivitanud paketi <strong>{$a->packname} {$a->packversion}</strong>. Õnnitleme!';
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['cannotcreatedboninstall'] = '<p>Ezin da datu-basea sortu.<p>
|
||||
<p>Zehaztutako datu-basea ez da existitzen eta adierazitako erabiltzaileak ez du baimenik datu-basea sortzeko.<p>
|
||||
$string['cannotcreatedboninstall'] = '<p>Ezin da datu-basea sortu.</p>
|
||||
<p>Zehaztutako datu-basea ez da existitzen eta adierazitako erabiltzaileak ez du baimenik datu-basea sortzeko.</p>
|
||||
<p>Guneko kudeatzaileak datu-basearen konfigurazioa egiaztatu behar du.</p>';
|
||||
$string['cannotcreatelangdir'] = 'Ezin da hizkuntza-direktorioa sortu';
|
||||
$string['cannotcreatetempdir'] = 'Ezin da temp direktorioa sortu';
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['language'] = 'Jezik';
|
||||
$string['moodlelogo'] = 'Moodle logo';
|
||||
$string['next'] = 'Nastavi';
|
||||
$string['previous'] = 'Prethodni';
|
||||
$string['reload'] = 'Učitaj ponovno';
|
||||
|
||||
@@ -35,7 +35,7 @@ $string['availablelangs'] = '利用可能な言語パック';
|
||||
$string['chooselanguagehead'] = '言語を選択してください。';
|
||||
$string['chooselanguagesub'] = 'インストールにのみ使用する言語を選択してください。この言語はサイトのデフォルト言語としても使用されます。後でサイト言語を変更することができます。';
|
||||
$string['clialreadyconfigured'] = '設定ファイルconfig.phpはすでに登録されています。このサイトをインストールする場合、admin/cli/install_database.phpを使用してください。';
|
||||
$string['clialreadyinstalled'] = '設定ファイルconfig.phpは、すでに登録されています。このサイトをアップグレードする場合、admin/cli/upgrade.phpを使用してください。';
|
||||
$string['clialreadyinstalled'] = '設定ファイルconfig.phpはすでに登録されています。このサイトをアップグレードする場合、admin/cli/upgrade.phpを使用してください。';
|
||||
$string['cliinstallheader'] = 'Moodle {$a} コマンドラインインストールプログラム';
|
||||
$string['databasehost'] = 'データベースホスト :';
|
||||
$string['databasename'] = 'データベース名 :';
|
||||
@@ -57,7 +57,7 @@ $string['memorylimithelp'] = '<p>現在、サーバのPHPメモリ制限が {$a}
|
||||
<ol>
|
||||
<li>あなたがリコンパイル可能な場合、PHPを<i>--enable-memory-limit</i>オプションでコンパイルしてください。これにより、Moodle自身がメモリ制限を設定することが可能になります。</li>
|
||||
<li>あなたがphp.iniファイルにアクセスできる場合、<b>memory_limit</b>設定を40Mのように変更することができます。php.iniファイルにアクセスできない場合、管理者に変更を依頼してください。</li>
|
||||
<li>いくつかのPHPサーバでは、下記の行を含む.htaccessファイルをMoodleディレクトリに作成することができます:
|
||||
<li>いくつかのPHPサーバでは下記の行を含む.htaccessファイルをMoodleディレクトリに作成することができます:
|
||||
<blockquote><div>php_value memory_limit 40M</div></blockquote>
|
||||
<p>しかし、この設定が<b>すべての</b>PHPページの動作を妨げる場合もあります。ページ閲覧中にエラーが表示される場合、.htaccessファイルを削除してください。</p></li>
|
||||
</ol>';
|
||||
|
||||
@@ -40,3 +40,4 @@ $string['cliunknowoption'] = 'Neatpažintos parinktys: {$a} naudokite --žinyno
|
||||
$string['cliyesnoprompt'] = 'įveskite t (taip) arba n (ne)';
|
||||
$string['environmentrequireinstall'] = 'turi būti įdiegta ir įjungta';
|
||||
$string['environmentrequireversion'] = 'būtina naudoti versiją {$a->needed}, o jūs naudojate {$a->current}';
|
||||
$string['upgradekeyset'] = 'Naujovinimo raktas (palikite tuščią, jei nenorite nustatyti)';
|
||||
|
||||
@@ -63,7 +63,9 @@ $string['pathsunsecuredataroot'] = 'Šakninio duomenų katalogo kelias nesaugus'
|
||||
$string['pathswrongadmindir'] = 'Nėra administratoriaus katalogo';
|
||||
$string['phpextension'] = '{$a} PHP plėtinys';
|
||||
$string['phpversion'] = 'PHP versija';
|
||||
$string['phpversionhelp'] = '<p>„Moodle“ reikalauja, kad PHP versija būtų bent 4.3.0 arba 5.1.0 (5.0.x versijoje yra daug problemų).</p> <p>Dabar naudojate versiją {$a}</p> <p>Turite atnaujinti PHP versiją arba perkelti į pagrindinį kompiuterį, kuriame veikia naujesnė PHP versija!<br /> (Jei naudojate 5.0.x versiją, taip pat galite paleisti senesnę 4.4.x versiją)</p>';
|
||||
$string['phpversionhelp'] = '<p>„Moodle“ reikalauja, kad PHP versija būtų ne žemesnė 5.6.5 arba 7.1 (7.0.x versijoje yra tam tikrų trūkumų).</p>
|
||||
<p>Dabar naudojate versiją {$a}</p>
|
||||
<p>Turite atnaujinti PHP versiją arba perkelti į pagrindinį kompiuterį, kuriame veikia naujesnė PHP versija.</p>';
|
||||
$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
|
||||
$string['welcomep20'] = 'Matote šį puslapį, nes į kompiuterį sėkmingai įdiegėte ir paleidote <strong>{$a->packname} {$a->packversion}</strong> paketą. Sveikiname!';
|
||||
$string['welcomep30'] = 'Šiame <strong>{$a->installername}</strong> leidime yra taikomosios programos, skirtos aplinkai, kurioje veiks <strong>Moodle</strong>, sukurti:';
|
||||
|
||||
@@ -30,6 +30,5 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['parentlanguage'] = '';
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Lietuvių';
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Automatically generated strings for Moodle installer
|
||||
*
|
||||
* Do not edit this file manually! It contains just a subset of strings
|
||||
* needed during the very first steps of installation. This file was
|
||||
* generated automatically by export-installer.php (which is part of AMOS
|
||||
* {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
|
||||
* list of strings defined in /install/stringnames.txt.
|
||||
*
|
||||
* @package installer
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['thisdirection'] = 'ltr';
|
||||
$string['thislanguage'] = 'Māori (Te Reo)';
|
||||
@@ -37,6 +37,9 @@ $string['cliincorrectvalueerror'] = 'Грешка, погрешна вредно
|
||||
$string['cliincorrectvalueretry'] = 'Неточни вредност, ве молиме обидете се повторно';
|
||||
$string['clitypevalue'] = 'впишете вредност';
|
||||
$string['clitypevaluedefault'] = 'впишете вредност, притиснете Enter за да го користите стандардната вредност ({$a})';
|
||||
$string['cliunknowoption'] = 'Непрепознаена опција:
|
||||
{$a}
|
||||
Ве молиме употребете --help опција.';
|
||||
$string['cliyesnoprompt'] = 'впишете y (значи да) или n (значи не)';
|
||||
$string['environmentrequireinstall'] = 'потребно е да се инсталира/овозможи';
|
||||
$string['environmentrequireversion'] = 'верзијата {$a->needed} е потребна, а вие извршувате {$a->current}';
|
||||
|
||||
@@ -35,11 +35,11 @@ $string['cliansweryes'] = 'y';
|
||||
$string['cliincorrectvalueerror'] = 'Error, valor incorrècta « {$a->value} » pel paramètre « {$a->option} »';
|
||||
$string['cliincorrectvalueretry'] = 'Valor incorrècta, ensajar tornamai';
|
||||
$string['clitypevalue'] = 'tipe valor';
|
||||
$string['clitypevaluedefault'] = 'tipe valor, tapez Entrée per utilizar la valor per defaut ({$a})';
|
||||
$string['clitypevaluedefault'] = 'tipe valor, picatz Entrada per utilizar la valor per defaut ({$a})';
|
||||
$string['cliunknowoption'] = 'Options non reconegudas :
|
||||
{$a}.
|
||||
Utilisez l\'option --help.';
|
||||
Utilizatz l\'opcion --help.';
|
||||
$string['cliyesnoprompt'] = 'Picatz y (per òc) o n (per non)';
|
||||
$string['environmentrequireinstall'] = 'doit èsser installat e activat';
|
||||
$string['environmentrequireinstall'] = 'deu èsser installat e activat';
|
||||
$string['environmentrequireversion'] = 'la version {$a->needed} es requesida ; utilizatz actualament la version {$a->current}';
|
||||
$string['upgradekeyset'] = 'Metre a jorn la clau (daissar void per la definir pas)';
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['cannotcreatedboninstall'] = '<p>Impossible de crear la basa de donadas.</p>
|
||||
<p>La banca de donadas indicada existís pas e l\'utilizaire especificat a pas las autorizacions que permeton de crear una basa de donadas.</p>.
|
||||
<p>La basa de donadas indicada existís pas e l\'utilizaire especificat a pas las autorizacions que permeton de crear una basa de donadas.</p>.
|
||||
<p>L\'administrator del site deu repassar la configuracion de la basa de donadas.</p>';
|
||||
$string['cannotcreatelangdir'] = 'Creacion del dorsièr lang impossible';
|
||||
$string['cannotcreatetempdir'] = 'Creacion del dorsièr temp impossibla';
|
||||
|
||||
@@ -34,9 +34,9 @@ $string['admindirname'] = 'Dorsièr d\'administracion';
|
||||
$string['availablelangs'] = 'Paquetatges de lenga disponibles';
|
||||
$string['chooselanguagehead'] = 'Causissètz una lenga';
|
||||
$string['cliinstallheader'] = 'Programa d\'installacion de Moodle {$a} en linha de comanda';
|
||||
$string['databasehost'] = 'Servidor de banca de donadas';
|
||||
$string['databasename'] = 'Nom de la banca de donadas';
|
||||
$string['databasetypehead'] = 'Seleccionar un pilòt de banca de donadas';
|
||||
$string['databasehost'] = 'Servidor de basa de donadas';
|
||||
$string['databasename'] = 'Nom de la basa de donadas';
|
||||
$string['databasetypehead'] = 'Seleccionar un pilòt de basa de donadas';
|
||||
$string['dataroot'] = 'Dorsièr de donadas';
|
||||
$string['dbprefix'] = 'Prefix de las taulas';
|
||||
$string['dirroot'] = 'Dorsièr Moodle';
|
||||
|
||||
@@ -72,5 +72,11 @@ $string['welcomep20'] = 'Vedeți această pagină deoarece ați instalat și lan
|
||||
$string['welcomep30'] = 'Lansarea <strong>{$a->installername}</strong> include aplicațiile
|
||||
pentru a crea un mediu în care <strong>Moodle</strong> va funcționa, și anume:';
|
||||
$string['welcomep40'] = 'Pachetul include și <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
|
||||
$string['welcomep50'] = 'Utilizarea tuturor aplicațiilor din acest pachet este determinată de respectivele lor
|
||||
licențe. Pachetul complet <strong>{$a->installername}</strong> este
|
||||
<a href="http://www.opensource.org/docs/definition_plain.html">open source</a> și este distribuit sub licența <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
|
||||
$string['welcomep60'] = 'Următoarele pagini vă oferă pași ușor de urmat pentru a
|
||||
configura și seta <strong>Moodle</strong> în computerul dumneavoastră. Puteți accepta setările implicite
|
||||
sau, opțional, să le modificați pentru a corespunde nevoilor dumneavoastră.';
|
||||
$string['welcomep70'] = 'Click pe butonul "Next" de mai jos pentru a continua setarea <strong>Moodle</strong>.';
|
||||
$string['wwwroot'] = 'Adresă Web';
|
||||
|
||||
@@ -31,3 +31,4 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$string['databasename'] = 'Databasens namn';
|
||||
$string['welcomep50'] = 'Användningen av alla applikationer i detta paket regleras av sina respektive licenser. Det fullständiga paketet <strong>{$a->installername}</strong> är <a href="http://www.opensource.org/docs/definition_plain.html">Öppen källkod</a> och distribueras under <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> licensen.';
|
||||
|
||||
+2
-1
@@ -137,7 +137,7 @@ $string['configallowblockstodock'] = 'If enabled and supported by the selected t
|
||||
$string['configallowcategorythemes'] = 'If you enable this, then themes can be set at the category level. This will affect all child categories and courses unless they have specifically set their own theme. WARNING: Enabling category themes may affect performance.';
|
||||
$string['configallowcoursethemes'] = 'If you enable this, then courses will be allowed to set their own themes. Course themes override all other theme choices (site, user, or session themes)';
|
||||
$string['configallowedemaildomains'] = 'List email domains that are allowed to be disclosed in the "From" section of outgoing email. The default of "Empty" will use the No-reply address for all outgoing email. The use of wildcards is allowed e.g. *.example.com will allow emails sent from any subdomain of example.com, but not example.com itself. This will require separate entry.';
|
||||
$string['configallowemailaddresses'] = 'If you want to restrict all new email addresses to particular domains, then list them here separated by spaces. All other domains will be rejected. To allow subdomains add the domain with a preceding \'.\'. eg <strong>ourcollege.edu.au .gov.au</strong>';
|
||||
$string['configallowemailaddresses'] = 'To restrict new email addresses to particular domains, list them here separated by spaces. All other domains will be rejected. To allow subdomains, add the domain with a preceding \'.\'. To allow a root domain together with its subdomains, add the domain twice - once with a preceding \'.\' and once without e.g. .ourcollege.edu.au ourcollege.edu.au.';
|
||||
$string['configallowguestmymoodle'] = 'If enabled guests can access Dashboard, otherwise guests are redirected to the site front page.';
|
||||
$string['configallowobjectembed'] = 'As a default security measure, normal users are not allowed to embed multimedia (like Flash) within texts using explicit EMBED and OBJECT tags in their HTML (although it can still be done safely using the mediaplugins filter). If you wish to allow these tags then enable this option.';
|
||||
$string['configallowoverride'] = 'You can allow people with the roles on the left side to override some of the column roles';
|
||||
@@ -1147,6 +1147,7 @@ $string['unsupporteddbstorageengine'] = 'The database storage engine being used
|
||||
$string['unsupporteddbtablerowformat'] = 'Your database has tables using Antelope as the file format. You are recommended to convert the tables to the Barracuda file format. See the documentation <a href="https://docs.moodle.org/en/cli">Administration via command line</a> for details of a tool for converting InnoDB tables to Barracuda.';
|
||||
$string['unsupportedphpversion7'] = 'PHP version 7 is not supported.';
|
||||
$string['unsupportedphpversion71'] = 'PHP version 7.1 is not supported.';
|
||||
$string['unsupportedphpversion72'] = 'PHP version 7.2 is not supported.';
|
||||
$string['unsuspenduser'] = 'Activate user account';
|
||||
$string['updateaccounts'] = 'Update existing accounts';
|
||||
$string['updatecomponent'] = 'Update component';
|
||||
|
||||
@@ -448,6 +448,8 @@ $string['notlocalisederrormessage'] = '{$a}';
|
||||
$string['notmemberofgroup'] = 'You are not a member of this course group';
|
||||
$string['notownerofkey'] = 'You are not owner of this key';
|
||||
$string['nousers'] = 'No such user!';
|
||||
$string['oauth1accesstoken'] = 'OAuth 1.0 error: We did not obtain the access token.';
|
||||
$string['oauth1requesttoken'] = 'OAuth 1.0 error: We did not obtain the request token - the service provider may be temporarily down.';
|
||||
$string['onlyadmins'] = 'Only administrators can do that';
|
||||
$string['onlyeditingteachers'] = 'Only editing teachers can do that';
|
||||
$string['onlyeditown'] = 'You can only edit your own information';
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ $string['duplicate_usernames'] = 'We failed to create an index on the columns "m
|
||||
$string['enabled_for_all'] = '(This service has been enabled for all hosts).';
|
||||
$string['enterausername'] = 'Please enter a username, or a list of usernames separated by commas.';
|
||||
$string['error7020'] = 'This error normally occurs if the remote site has created a record for you with the wrong wwwroot, for example, http://yoursite.com instead of http://www.yoursite.com. You should contact the administrator of the remote site with your wwwroot (as specified in config.php) asking her to update her record for your host.';
|
||||
$string['error7022'] = 'The message you sent to the remote site was encrypted properly, but not signed. This is very unexpected; you should probably file a bug if this occurs (giving as much information as possible about the application versions in question, etc.';
|
||||
$string['error7022'] = 'The message you sent to the remote site was encrypted properly, but not signed. This is very unexpected; you should probably file a bug if this occurs (giving as much information as possible about the application versions in question etc).';
|
||||
$string['error7023'] = 'The remote site has tried to decrypt your message with all the keys it has on record for your site. They have all failed. You might be able to fix this problem by manually re-keying with the remote site. This is unlikely to occur unless you\'ve been out of communication with the remote site for a few months.';
|
||||
$string['error7024'] = 'You send an unencrypted message to the remote site, but the remote site doesn\'t accept unencrypted communication from your site. This is very unexpected; you should probably file a bug if this occurs (giving as much information as possible about the application versions in question, etc.';
|
||||
$string['error7026'] = 'The key that your message was signed with differs from the key that the remote host has on file for your server. Further, the remote host attempted to fetch your current key and failed to do so. Please manually re-key with the remote host and try again.';
|
||||
|
||||
+4
-2
@@ -618,6 +618,8 @@ $string['emaildisplayyes'] = 'Allow everyone to see my email address';
|
||||
$string['emailenable'] = 'This email address is enabled';
|
||||
$string['emailenableclick'] = 'Click here to re-enable all email being sent to this address';
|
||||
$string['emailexists'] = 'This email address is already registered.';
|
||||
$string['emailexistssignuphint'] = 'Perhaps you created an account in the past? {$a}';
|
||||
$string['emailexistshintlink'] = 'Recover forgotten username or password';
|
||||
$string['emailformat'] = 'Email format';
|
||||
$string['emailcharset'] = 'Email charset';
|
||||
$string['emailmustbereal'] = 'Note: your email address must be a real one';
|
||||
@@ -709,7 +711,7 @@ $string['emptydragdropregion'] = 'empty region';
|
||||
$string['enable'] = 'Enable';
|
||||
$string['encryptedcode'] = 'Encrypted code';
|
||||
$string['enddate'] = 'Course end date';
|
||||
$string['enddate_help'] = 'The course end date is only used for reports. Users can still enter the course after the end date.';
|
||||
$string['enddate_help'] = 'The course end date is used for determining whether a course should be included in a user\'s list of courses. When the end date is past, the course is no longer listed in the navigation and is listed as past in the course overview. The course end date may also be used by custom reports. Users can still enter the course after the end date; in other words the date does not restrict access.';
|
||||
$string['english'] = 'English';
|
||||
$string['enrolmentmethods'] = 'Enrolment methods';
|
||||
$string['entercourse'] = 'Click to enter this course';
|
||||
@@ -978,7 +980,7 @@ $string['choosereportfilter'] = 'Choose a filter for the report';
|
||||
$string['choosetheme'] = 'Choose theme';
|
||||
$string['chooseuser'] = 'Choose a user';
|
||||
$string['courseduration'] = 'Course duration';
|
||||
$string['courseduration_desc'] = 'The course duration is used to calculate the default course end date. The course end date is only used for reports. Users can still enter the course after the end date.';
|
||||
$string['courseduration_desc'] = 'The course duration is used to calculate the default course end date. The course end date is used for determining whether a course should be included in a user\'s list of courses. It may also be used by custom reports. Users can still enter the course after the end date.';
|
||||
$string['eventcontentviewed'] = 'Content viewed';
|
||||
$string['icqnumber'] = 'ICQ number';
|
||||
$string['icon'] = 'Icon';
|
||||
|
||||
+1
-1
@@ -350,7 +350,7 @@ $string['restore:restoretargetimport'] = 'Restore from files targeted as import'
|
||||
$string['restore:rolldates'] = 'Allowed to roll activity configuration dates on restore';
|
||||
$string['restore:uploadfile'] = 'Upload files to backup areas';
|
||||
$string['restore:userinfo'] = 'Restore user data';
|
||||
$string['restore:viewautomatedfilearea'] = 'View automated backup area';
|
||||
$string['restore:viewautomatedfilearea'] = 'Restore courses from automated backups';
|
||||
$string['risks'] = 'Risks';
|
||||
$string['roleallowheader'] = 'Allow role:';
|
||||
$string['roleallowinfo'] = 'Select a role to be added to the list of allowed roles in context "{$a->context}", capability "{$a->cap}":';
|
||||
|
||||
+25
-12
@@ -2743,20 +2743,28 @@ function get_component_string($component, $contextlevel) {
|
||||
|
||||
/**
|
||||
* Gets the list of roles assigned to this context and up (parents)
|
||||
* from the list of roles that are visible on user profile page
|
||||
* and participants page.
|
||||
* from the aggregation of:
|
||||
* a) the list of roles that are visible on user profile page and participants page (profileroles setting) and;
|
||||
* b) if applicable, those roles that are assigned in the context.
|
||||
*
|
||||
* @param context $context
|
||||
* @return array
|
||||
*/
|
||||
function get_profile_roles(context $context) {
|
||||
global $CFG, $DB;
|
||||
|
||||
if (empty($CFG->profileroles)) {
|
||||
return array();
|
||||
// If the current user can assign roles, then they can see all roles on the profile and participants page,
|
||||
// provided the roles are assigned to at least 1 user in the context. If not, only the policy-defined roles.
|
||||
if (has_capability('moodle/role:assign', $context)) {
|
||||
$rolesinscope = array_keys(get_all_roles($context));
|
||||
} else {
|
||||
$rolesinscope = empty($CFG->profileroles) ? [] : array_map('trim', explode(',', $CFG->profileroles));
|
||||
}
|
||||
|
||||
list($rallowed, $params) = $DB->get_in_or_equal(explode(',', $CFG->profileroles), SQL_PARAMS_NAMED, 'a');
|
||||
if (empty($rolesinscope)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
list($rallowed, $params) = $DB->get_in_or_equal($rolesinscope, SQL_PARAMS_NAMED, 'a');
|
||||
list($contextlist, $cparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'p');
|
||||
$params = array_merge($params, $cparams);
|
||||
|
||||
@@ -2815,18 +2823,23 @@ function get_roles_used_in_context(context $context) {
|
||||
*/
|
||||
function get_user_roles_in_course($userid, $courseid) {
|
||||
global $CFG, $DB;
|
||||
|
||||
if (empty($CFG->profileroles)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($courseid == SITEID) {
|
||||
$context = context_system::instance();
|
||||
} else {
|
||||
$context = context_course::instance($courseid);
|
||||
}
|
||||
// If the current user can assign roles, then they can see all roles on the profile and participants page,
|
||||
// provided the roles are assigned to at least 1 user in the context. If not, only the policy-defined roles.
|
||||
if (has_capability('moodle/role:assign', $context)) {
|
||||
$rolesinscope = array_keys(get_all_roles($context));
|
||||
} else {
|
||||
$rolesinscope = empty($CFG->profileroles) ? [] : array_map('trim', explode(',', $CFG->profileroles));
|
||||
}
|
||||
if (empty($rolesinscope)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
list($rallowed, $params) = $DB->get_in_or_equal(explode(',', $CFG->profileroles), SQL_PARAMS_NAMED, 'a');
|
||||
list($rallowed, $params) = $DB->get_in_or_equal($rolesinscope, SQL_PARAMS_NAMED, 'a');
|
||||
list($contextlist, $cparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'p');
|
||||
$params = array_merge($params, $cparams);
|
||||
|
||||
|
||||
+6
-16
@@ -3702,16 +3702,11 @@ class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
|
||||
$entries = explode("\n", $data);
|
||||
foreach ($entries as $key => $entry) {
|
||||
$entry = trim($entry);
|
||||
// This regex matches any string which:
|
||||
// a) contains at least one non-ascii unicode character AND
|
||||
// b) starts with a-zA-Z0-9 or any non-ascii unicode character AND
|
||||
// c) ends with a-zA-Z0-9 or any non-ascii unicode character
|
||||
// d) contains a-zA-Z0-9, hyphen, dot or any non-ascii unicode characters in the middle.
|
||||
if (preg_match('/^(?=[^\x00-\x7f])([^\x00-\x7f]|[a-zA-Z0-9])([^\x00-\x7f]|[a-zA-Z0-9-.])*([^\x00-\x7f]|[a-zA-Z0-9])$/',
|
||||
$entry)) {
|
||||
// This regex matches any string that has non-ascii character.
|
||||
if (preg_match('/[^\x00-\x7f]/', $entry)) {
|
||||
// If we can convert the unicode string to an idn, do so.
|
||||
// Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
|
||||
$val = idn_to_ascii($entry);
|
||||
$val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||
$entries[$key] = $val ? $val : $entry;
|
||||
}
|
||||
}
|
||||
@@ -3729,7 +3724,7 @@ class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
|
||||
foreach ($entries as $key => $entry) {
|
||||
$entry = trim($entry);
|
||||
if (strpos($entry, 'xn--') !== false) {
|
||||
$entries[$key] = idn_to_utf8($entry);
|
||||
$entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||
}
|
||||
}
|
||||
return implode("\n", $entries);
|
||||
@@ -8746,16 +8741,11 @@ class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
|
||||
* @return string XHTML
|
||||
*/
|
||||
public function output_html($data, $query='') {
|
||||
global $CFG, $OUTPUT;
|
||||
global $OUTPUT;
|
||||
$html = parent::output_html($data, $query);
|
||||
|
||||
if ((string)$data === $this->yes) {
|
||||
require_once($CFG->dirroot . "/lib/filelib.php");
|
||||
$curl = new curl();
|
||||
$httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); //force https url
|
||||
$curl->head($httpswwwroot . "/login/index.php");
|
||||
$info = $curl->get_info();
|
||||
if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
|
||||
if (!is_https()) {
|
||||
$html .= $OUTPUT->notification(get_string('nohttpsformobilewarning', 'admin'));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/ajax","core/templates","core/notification","core/str","core/config","core/url"],function(a,b,c,d,e,f,g){return a("body").on("click keypress","[data-inplaceeditable] [data-inplaceeditablelink]",function(h){if("keypress"!==h.type||13===h.keyCode){h.stopImmediatePropagation(),h.preventDefault();var i=a(this),j=i.closest("[data-inplaceeditable]"),k=function(b){b.addClass("updating");var c=b.find("img.spinner");c.length?c.show():(c=a("<img/>").attr("src",g.imageUrl("i/loading_small")).addClass("spinner").addClass("smallicon"),b.append(c))},l=function(a){a.removeClass("updating"),a.find("img.spinner").hide()},m=function(e,f){k(e),b.call([{methodname:"core_update_inplace_editable",args:{itemid:e.attr("data-itemid"),component:e.attr("data-component"),itemtype:e.attr("data-itemtype"),value:f},done:function(b){var d=e.attr("data-value");c.render("core/inplace_editable",b).done(function(f,g){var h=a(f);c.replaceNode(e,h,g),h.find("[data-inplaceeditablelink]").focus(),h.trigger({type:"updated",ajaxreturn:b,oldvalue:d})})},fail:function(b){var c=a.Event("updatefailed",{exception:b,newvalue:f});l(e),e.trigger(c),c.isDefaultPrevented()||d.exception(b)}}],!0)},n=function(a){a.find("input").off(),a.find("select").off(),a.html(a.attr("data-oldcontent")),a.removeAttr("data-oldcontent"),a.removeClass("inplaceeditingon"),a.find("[data-inplaceeditablelink]").focus()},o=function(){a("span.inplaceeditable.inplaceeditingon").each(function(){n(a(this))})},p=function(b,c){var d,e=b;for(d=0;d<c;d++)e+=String(Math.floor(10*Math.random()));return 0===a("#"+e).length?e:p(b,c)},q=function(b){e.get_string("edittitleinstructions").done(function(c){var d=a('<span class="editinstructions">'+c+"</span>").attr("id",p("id_editinstructions_",20)),e=a('<input type="text"/>').attr("id",p("id_inplacevalue_",20)).attr("value",b.attr("data-value")).attr("aria-describedby",d.attr("id")).addClass("ignoredirty").addClass("form-control"),g=a('<label class="accesshide">'+j.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));b.html("").append(d).append(g).append(e),e.focus(),e.select(),e.on("keyup keypress focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("keypress"===a.type&&13===a.keyCode){var c=e.val();n(b),m(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&n(b)}})})},r=function(a,b){n(a),m(a,b)},s=function(b,c){var d,e=a("<select></select>").attr("id",p("id_inplacevalue_",20)).addClass("custom-select"),g=a('<label class="accesshide">'+j.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));for(d in c)e.append(a("<option>").attr("value",c[d].key).html(c[d].value));e.val(b.attr("data-value")),b.html("").append(g).append(e),e.focus(),e.select(),e.on("keyup change focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("change"===a.type){var c=e.val();n(b),m(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&n(b)}})},t=function(b){b.addClass("inplaceeditingon"),b.attr("data-oldcontent",b.html());var c=b.attr("data-type"),d=b.attr("data-options");"toggle"===c?r(b,d):"select"===c?s(b,a.parseJSON(d)):q(b)};o(),t(j)}}),{}});
|
||||
define(["jquery","core/ajax","core/templates","core/notification","core/str","core/config","core/url"],function(a,b,c,d,e,f,g){return a("body").on("click keypress","[data-inplaceeditable] [data-inplaceeditablelink]",function(h){if("keypress"!==h.type||13===h.keyCode){h.stopImmediatePropagation(),h.preventDefault();var i=a(this),j=i.closest("[data-inplaceeditable]"),k=function(b){b.addClass("updating");var c=b.find("img.spinner");c.length?c.show():(c=a("<img/>").attr("src",g.imageUrl("i/loading_small")).addClass("spinner").addClass("smallicon"),b.append(c))},l=function(a){a.removeClass("updating"),a.find("img.spinner").hide()},m=function(e,f){var g=[e.attr("data-itemid"),e.attr("data-component"),e.attr("data-itemtype")].join("-");M.util.js_pending(g),k(e),b.call([{methodname:"core_update_inplace_editable",args:{itemid:e.attr("data-itemid"),component:e.attr("data-component"),itemtype:e.attr("data-itemtype"),value:f},done:function(b){var d=e.attr("data-value");c.render("core/inplace_editable",b).done(function(f,h){var i=a(f);c.replaceNode(e,i,h),i.find("[data-inplaceeditablelink]").focus(),i.trigger({type:"updated",ajaxreturn:b,oldvalue:d}),M.util.js_complete(g)})},fail:function(b){var c=a.Event("updatefailed",{exception:b,newvalue:f});l(e),M.util.js_complete(g),e.trigger(c),c.isDefaultPrevented()||d.exception(b)}}],!0)},n=function(a){a.find("input").off(),a.find("select").off(),a.html(a.attr("data-oldcontent")),a.removeAttr("data-oldcontent"),a.removeClass("inplaceeditingon"),a.find("[data-inplaceeditablelink]").focus()},o=function(){a("span.inplaceeditable.inplaceeditingon").each(function(){n(a(this))})},p=function(b,c){var d,e=b;for(d=0;d<c;d++)e+=String(Math.floor(10*Math.random()));return 0===a("#"+e).length?e:p(b,c)},q=function(b){e.get_string("edittitleinstructions").done(function(c){var d=a('<span class="editinstructions">'+c+"</span>").attr("id",p("id_editinstructions_",20)),e=a('<input type="text"/>').attr("id",p("id_inplacevalue_",20)).attr("value",b.attr("data-value")).attr("aria-describedby",d.attr("id")).addClass("ignoredirty").addClass("form-control"),g=a('<label class="accesshide">'+j.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));b.html("").append(d).append(g).append(e),e.focus(),e.select(),e.on("keyup keypress focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("keypress"===a.type&&13===a.keyCode){var c=e.val();n(b),m(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&n(b)}})})},r=function(a,b){n(a),m(a,b)},s=function(b,c){var d,e=a("<select></select>").attr("id",p("id_inplacevalue_",20)).addClass("custom-select"),g=a('<label class="accesshide">'+j.attr("data-editlabel")+"</label>").attr("for",e.attr("id"));for(d in c)e.append(a("<option>").attr("value",c[d].key).html(c[d].value));e.val(b.attr("data-value")),b.html("").append(g).append(e),e.focus(),e.select(),e.on("keyup change focusout",function(a){if(!f.behatsiterunning||"focusout"!==a.type){if("change"===a.type){var c=e.val();n(b),m(b,c)}("keyup"===a.type&&27===a.keyCode||"focusout"===a.type)&&n(b)}})},t=function(b){b.addClass("inplaceeditingon"),b.attr("data-oldcontent",b.html());var c=b.attr("data-type"),d=b.attr("data-options");"toggle"===c?r(b,d):"select"===c?s(b,a.parseJSON(d)):q(b)};o(),t(j)}}),{}});
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
||||
define(["jquery","core/modal_events","core/modal_registry","core/modal","core/modal_save_cancel","core/modal_confirm","core/modal_cancel","core/templates","core/notification","core/custom_interaction_events"],function(a,b,c,d,e,f,g,h,i,j){var k={DEFAULT:"core/modal",SAVE_CANCEL:"core/modal_save_cancel",CONFIRM:"core/modal_confirm",CANCEL:"core/modal_cancel"},l={DEFAULT:"DEFAULT",SAVE_CANCEL:"SAVE_CANCEL",CONFIRM:"CONFIRM",CANCEL:"CANCEL"};c.register(l.DEFAULT,d,k.DEFAULT),c.register(l.SAVE_CANCEL,e,k.SAVE_CANCEL),c.register(l.CONFIRM,f,k.CONFIRM),c.register(l.CANCEL,g,k.CANCEL);var m=function(a,c){"undefined"!=typeof c&&(j.define(c,[j.events.activate]),c.on(j.events.activate,function(b,c){a.then(function(a){return a.show(),a}),c.originalEvent.preventDefault()}),a.then(function(a){return a.getRoot().on(b.hidden,function(){c.focus()}),a}))},n=function(b,c){c=a(c);var d=b.module,e=new d(c);return e},o=function(b,c){var d=b.template,e=h.render(d,{}).then(function(c){var d=a(c);return n(b,d)}).fail(i.exception);return m(e,c),e},p=function(a,b){var d=a.type||l.DEFAULT,e=!!a.large,f=null;return f=c.get(d),f||i.exception({message:"Unable to find modal of type: "+d}),o(f,b).then(function(b){return"undefined"!=typeof a.title&&b.setTitle(a.title),"undefined"!=typeof a.body&&b.setBody(a.body),"undefined"!=typeof a.footer&&b.setFooter(a.footer),e&&b.setLarge(),b})};return{create:p,types:l}});
|
||||
define(["jquery","core/modal_events","core/modal_registry","core/modal","core/modal_save_cancel","core/modal_confirm","core/modal_cancel","core/templates","core/notification","core/custom_interaction_events"],function(a,b,c,d,e,f,g,h,i,j){var k={DEFAULT:"core/modal",SAVE_CANCEL:"core/modal_save_cancel",CONFIRM:"core/modal_confirm",CANCEL:"core/modal_cancel"},l={DEFAULT:"DEFAULT",SAVE_CANCEL:"SAVE_CANCEL",CONFIRM:"CONFIRM",CANCEL:"CANCEL"};c.register(l.DEFAULT,d,k.DEFAULT),c.register(l.SAVE_CANCEL,e,k.SAVE_CANCEL),c.register(l.CONFIRM,f,k.CONFIRM),c.register(l.CANCEL,g,k.CANCEL);var m=function(a,c){if("undefined"!=typeof c){var d=null;j.define(c,[j.events.activate]),c.on(j.events.activate,function(b,c){d=b.currentTarget,a.then(function(a){return a.show(),a}),c.originalEvent.preventDefault()}),a.then(function(a){return a.getRoot().on(b.hidden,function(){null!==d&&d.focus()}),a})}},n=function(b,c){c=a(c);var d=b.module,e=new d(c);return e},o=function(b,c){var d=b.template,e=h.render(d,{}).then(function(c){var d=a(c);return n(b,d)}).fail(i.exception);return m(e,c),e},p=function(a,b){var d=a.type||l.DEFAULT,e=!!a.large,f=null;return f=c.get(d),f||i.exception({message:"Unable to find modal of type: "+d}),o(f,b).then(function(b){return"undefined"!=typeof a.title&&b.setTitle(a.title),"undefined"!=typeof a.body&&b.setBody(a.body),"undefined"!=typeof a.footer&&b.setFooter(a.footer),e&&b.setLarge(),b})};return{create:p,types:l}});
|
||||
@@ -37,6 +37,8 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
UP: 38
|
||||
};
|
||||
|
||||
var uniqueId = $.now();
|
||||
|
||||
/**
|
||||
* Make an item in the selection list "active".
|
||||
*
|
||||
@@ -774,11 +776,15 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Find or generate some ids.
|
||||
var state = {
|
||||
selectId: originalSelect.attr('id'),
|
||||
inputId: 'form_autocomplete_input-' + $.now(),
|
||||
suggestionsId: 'form_autocomplete_suggestions-' + $.now(),
|
||||
selectionId: 'form_autocomplete_selection-' + $.now(),
|
||||
downArrowId: 'form_autocomplete_downarrow-' + $.now()
|
||||
inputId: 'form_autocomplete_input-' + uniqueId,
|
||||
suggestionsId: 'form_autocomplete_suggestions-' + uniqueId,
|
||||
selectionId: 'form_autocomplete_selection-' + uniqueId,
|
||||
downArrowId: 'form_autocomplete_downarrow-' + uniqueId
|
||||
};
|
||||
|
||||
// Increment the unique counter so we don't get duplicates ever.
|
||||
uniqueId++;
|
||||
|
||||
options.multiple = originalSelect.attr('multiple');
|
||||
|
||||
var originalLabel = $('[for=' + state.selectId + ']');
|
||||
|
||||
@@ -60,6 +60,13 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
|
||||
};
|
||||
|
||||
var updateValue = function(mainelement, value) {
|
||||
var pendingId = [
|
||||
mainelement.attr('data-itemid'),
|
||||
mainelement.attr('data-component'),
|
||||
mainelement.attr('data-itemtype'),
|
||||
].join('-');
|
||||
M.util.js_pending(pendingId);
|
||||
|
||||
addSpinner(mainelement);
|
||||
ajax
|
||||
.call([{
|
||||
@@ -77,6 +84,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
|
||||
templates.replaceNode(mainelement, newelement, js);
|
||||
newelement.find('[data-inplaceeditablelink]').focus();
|
||||
newelement.trigger({type: 'updated', ajaxreturn: data, oldvalue: oldvalue});
|
||||
M.util.js_complete(pendingId);
|
||||
});
|
||||
},
|
||||
fail: function(ex) {
|
||||
@@ -85,6 +93,7 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str'
|
||||
newvalue: value
|
||||
});
|
||||
removeSpinner(mainelement);
|
||||
M.util.js_complete(pendingId);
|
||||
mainelement.trigger(e);
|
||||
if (!e.isDefaultPrevented()) {
|
||||
notification.exception(ex);
|
||||
|
||||
+105
-17
@@ -50,6 +50,12 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
*/
|
||||
var backdropPromise;
|
||||
|
||||
/**
|
||||
* A counter that gets incremented for each modal created. This can be
|
||||
* used to generate unique values for the modals.
|
||||
*/
|
||||
var modalCounter = 0;
|
||||
|
||||
/**
|
||||
* Constructor for the Modal.
|
||||
*
|
||||
@@ -66,6 +72,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
this.isAttached = false;
|
||||
this.bodyJS = null;
|
||||
this.footerJS = null;
|
||||
this.modalCount = modalCounter++;
|
||||
|
||||
if (!this.root.is(SELECTORS.CONTAINER)) {
|
||||
Notification.exception({message: 'Element is not a modal container'});
|
||||
@@ -212,6 +219,16 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
return this.footer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the unique modal count.
|
||||
*
|
||||
* @method getModalCount
|
||||
* @return {int}
|
||||
*/
|
||||
Modal.prototype.getModalCount = function() {
|
||||
return this.modalCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the modal title element.
|
||||
*
|
||||
@@ -253,25 +270,96 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
// Just set the value if it's a string.
|
||||
body.html(value);
|
||||
} else {
|
||||
var jsPendingId = 'amd-modal-js-pending-id-' + this.getModalCount();
|
||||
M.util.js_pending(jsPendingId);
|
||||
// Otherwise we assume it's a promise to be resolved with
|
||||
// html and javascript.
|
||||
Templates.render(TEMPLATES.LOADING, {}).done(function(html) {
|
||||
body.html(html);
|
||||
var contentPromise = null;
|
||||
body.css('overflow', 'hidden');
|
||||
|
||||
value.done(function(html, js) {
|
||||
if (value.state() == 'pending') {
|
||||
// We're still waiting for the body promise to resolve so
|
||||
// let's show a loading icon.
|
||||
body.animate({height: '100px'}, 150);
|
||||
|
||||
body.html('');
|
||||
contentPromise = Templates.render(TEMPLATES.LOADING, {})
|
||||
.then(function(html) {
|
||||
var loadingIcon = $(html).hide();
|
||||
body.html(loadingIcon);
|
||||
loadingIcon.fadeIn(150);
|
||||
|
||||
// We only want the loading icon to fade out
|
||||
// when the content for the body has finished
|
||||
// loading.
|
||||
return $.when(loadingIcon.promise(), value);
|
||||
})
|
||||
.then(function(loadingIcon) {
|
||||
// Once the content has finished loading and
|
||||
// the loading icon has been shown then we can
|
||||
// fade the icon away to reveal the content.
|
||||
return loadingIcon.fadeOut(100).promise();
|
||||
})
|
||||
.then(function() {
|
||||
return value;
|
||||
});
|
||||
} else {
|
||||
// The content is already loaded so let's just display
|
||||
// it to the user. No need for a loading icon.
|
||||
contentPromise = value;
|
||||
}
|
||||
|
||||
// Now we can actually display the content.
|
||||
contentPromise.then(function(html, js) {
|
||||
var result = null;
|
||||
|
||||
if (this.isVisible()) {
|
||||
// If the modal is visible then we should display
|
||||
// the content gracefully for the user.
|
||||
body.css('opacity', 0);
|
||||
var currentHeight = body.innerHeight();
|
||||
body.html(html);
|
||||
// We need to clear any height values we've set here
|
||||
// in order to measure the height of the content being
|
||||
// added. This then allows us to animate the height
|
||||
// transition.
|
||||
body.css('height', '');
|
||||
var newHeight = body.innerHeight();
|
||||
body.css('height', currentHeight + 'px');
|
||||
result = body.animate(
|
||||
{height: newHeight + 'px', opacity: 1},
|
||||
{duration: 150, queue: false}
|
||||
).promise();
|
||||
} else {
|
||||
// Since the modal isn't visible we can just immediately
|
||||
// set the content. No need to animate it.
|
||||
body.html(html);
|
||||
}
|
||||
|
||||
if (js) {
|
||||
if (this.isAttached) {
|
||||
// If we're in the DOM then run the JS immediately.
|
||||
Templates.runTemplateJS(js);
|
||||
} else {
|
||||
// Otherwise cache it to be run when we're attached.
|
||||
this.bodyJS = js;
|
||||
}
|
||||
if (js) {
|
||||
if (this.isAttached) {
|
||||
// If we're in the DOM then run the JS immediately.
|
||||
Templates.runTemplateJS(js);
|
||||
} else {
|
||||
// Otherwise cache it to be run when we're attached.
|
||||
this.bodyJS = js;
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
return result;
|
||||
}.bind(this))
|
||||
.fail(Notification.exception)
|
||||
.always(function() {
|
||||
// When we're done displaying all of the content we need
|
||||
// to clear the custom values we've set here.
|
||||
body.css('height', '');
|
||||
body.css('overflow', '');
|
||||
body.css('opacity', '');
|
||||
M.util.js_complete(jsPendingId);
|
||||
|
||||
return;
|
||||
})
|
||||
.fail(Notification.exception);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -324,7 +412,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
return;
|
||||
}
|
||||
|
||||
this.getRoot().addClass('large');
|
||||
this.getModal().addClass('modal-lg');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -334,7 +422,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
* @return {bool}
|
||||
*/
|
||||
Modal.prototype.isLarge = function() {
|
||||
return this.getRoot().hasClass('large');
|
||||
return this.getModal().hasClass('modal-lg');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -347,7 +435,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
return;
|
||||
}
|
||||
|
||||
this.getRoot().removeClass('large');
|
||||
this.getModal().removeClass('modal-lg');
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -357,7 +445,7 @@ define(['jquery', 'core/templates', 'core/notification', 'core/key_codes',
|
||||
* @return {bool}
|
||||
*/
|
||||
Modal.prototype.isSmall = function() {
|
||||
return !this.getRoot().hasClass('large');
|
||||
return !this.getModal().hasClass('modal-lg');
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,8 +60,11 @@ define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
|
||||
*/
|
||||
var setUpTrigger = function(modalPromise, triggerElement) {
|
||||
if (typeof triggerElement != 'undefined') {
|
||||
// The element that actually shows the modal.
|
||||
var actualTriggerElement = null;
|
||||
CustomEvents.define(triggerElement, [CustomEvents.events.activate]);
|
||||
triggerElement.on(CustomEvents.events.activate, function(e, data) {
|
||||
actualTriggerElement = e.currentTarget;
|
||||
modalPromise.then(function(modal) {
|
||||
modal.show();
|
||||
|
||||
@@ -72,7 +75,10 @@ define(['jquery', 'core/modal_events', 'core/modal_registry', 'core/modal',
|
||||
|
||||
modalPromise.then(function(modal) {
|
||||
modal.getRoot().on(ModalEvents.hidden, function() {
|
||||
triggerElement.focus();
|
||||
// Focus on the trigger element that actually launched the modal.
|
||||
if (actualTriggerElement !== null) {
|
||||
actualTriggerElement.focus();
|
||||
}
|
||||
});
|
||||
|
||||
return modal;
|
||||
|
||||
+3
-1
@@ -887,7 +887,9 @@ function signup_validate_data($data, $files) {
|
||||
$errors['email'] = get_string('invalidemail');
|
||||
|
||||
} else if ($DB->record_exists('user', array('email' => $data['email']))) {
|
||||
$errors['email'] = get_string('emailexists').' <a href="forgot_password.php">'.get_string('newpassword').'?</a>';
|
||||
$errors['email'] = get_string('emailexists') . ' ' .
|
||||
get_string('emailexistssignuphint', 'moodle',
|
||||
html_writer::link(new moodle_url('/login/forgot_password.php'), get_string('emailexistshintlink')));
|
||||
}
|
||||
if (empty($data['email2'])) {
|
||||
$errors['email2'] = get_string('missingemail');
|
||||
|
||||
@@ -131,6 +131,8 @@ XPATH
|
||||
]) = %locator%] |
|
||||
.//div[contains(concat(' ', normalize-space(@class), ' '), ' yui-dialog ') and
|
||||
normalize-space(descendant::div[@class='hd']) = %locator%]
|
||||
|
|
||||
.//div[@data-region='modal' and descendant::*[@data-region='title'] = %locator%]
|
||||
XPATH
|
||||
, 'icon' => <<<XPATH
|
||||
.//*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') and ( contains(normalize-space(@title), %locator%))]
|
||||
|
||||
@@ -70,7 +70,23 @@ class user_login_failed extends base {
|
||||
public function get_description() {
|
||||
// Note that username could be any random user input.
|
||||
$username = s($this->other['username']);
|
||||
return "Login failed for the username '{$username}' for the reason with id '{$this->other['reason']}'.";
|
||||
$reasonid = $this->other['reason'];
|
||||
$loginfailed = 'Login failed for user';
|
||||
switch ($reasonid){
|
||||
case 1:
|
||||
return $loginfailed." '{$username}'. User does not exist (error ID '{$reasonid}').";
|
||||
case 2:
|
||||
return $loginfailed." '{$username}'. User is suspended (error ID '{$reasonid}').";
|
||||
case 3:
|
||||
return $loginfailed." '{$username}'. Most likely the password did not match (error ID '{$reasonid}').";
|
||||
case 4:
|
||||
return $loginfailed." '{$username}'. User is locked out (error ID '{$reasonid}').";
|
||||
case 5:
|
||||
return $loginfailed." '{$username}'. User is not authorised (error ID '{$reasonid}').";
|
||||
default:
|
||||
return $loginfailed." '{$username}', error ID '{$reasonid}'.";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -215,15 +215,9 @@ class client extends \oauth2_client {
|
||||
$this->store_token($accesstoken);
|
||||
|
||||
if (isset($r->refresh_token)) {
|
||||
$userinfo = $this->get_userinfo();
|
||||
|
||||
if ($userinfo['email'] == $systemaccount->get('email')) {
|
||||
$systemaccount->set('refreshtoken', $r->refresh_token);
|
||||
$systemaccount->update();
|
||||
$this->refreshtoken = $r->refresh_token;
|
||||
} else {
|
||||
throw new moodle_exception('Attempt to store refresh token for non-system user.');
|
||||
}
|
||||
$systemaccount->set('refreshtoken', $r->refresh_token);
|
||||
$systemaccount->update();
|
||||
$this->refreshtoken = $r->refresh_token;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -145,5 +145,12 @@ abstract class icon_system {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the instance cache, for use in unit tests
|
||||
*/
|
||||
public static function reset_caches() {
|
||||
self::$instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,18 +36,29 @@ defined('MOODLE_INTERNAL') || die();
|
||||
class memcached extends handler {
|
||||
/** @var string $savepath save_path string */
|
||||
protected $savepath;
|
||||
|
||||
/** @var array $servers list of servers parsed from save_path */
|
||||
protected $servers;
|
||||
|
||||
/** @var string $prefix session key prefix */
|
||||
protected $prefix;
|
||||
|
||||
/** @var int $acquiretimeout how long to wait for session lock */
|
||||
protected $acquiretimeout = 120;
|
||||
|
||||
/**
|
||||
* @var int $lockexpire how long to wait before expiring the lock so that other requests
|
||||
* may continue execution, ignored if PECL memcached is below version 2.2.0.
|
||||
*/
|
||||
protected $lockexpire = 7200;
|
||||
|
||||
/**
|
||||
* @var integer $lockretrysleep Used for memcached 3.x (PHP7), the amount of time to
|
||||
* sleep between attempts to acquire the session lock. Mimics the deprecated config
|
||||
* memcached.sess_lock_wait.
|
||||
*/
|
||||
protected $lockretrysleep = 150;
|
||||
|
||||
/**
|
||||
* Create new instance of handler.
|
||||
*/
|
||||
@@ -79,6 +90,10 @@ class memcached extends handler {
|
||||
if (!empty($CFG->session_memcached_lock_expire)) {
|
||||
$this->lockexpire = (int)$CFG->session_memcached_lock_expire;
|
||||
}
|
||||
|
||||
if (!empty($CFG->session_memcached_lock_retry_sleep)) {
|
||||
$this->lockretrysleep = (int)$CFG->session_memcached_lock_retry_sleep;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,15 +149,32 @@ class memcached extends handler {
|
||||
ini_set('session.save_path', $this->savepath);
|
||||
ini_set('memcached.sess_prefix', $this->prefix);
|
||||
ini_set('memcached.sess_locking', '1'); // Locking is required!
|
||||
ini_set('memcached.sess_lock_expire', $this->lockexpire);
|
||||
|
||||
// Try to configure lock and expire timeouts - ignored if memcached is before version 2.2.0.
|
||||
if (version_compare($version, '3.0.0-dev') >= 0) {
|
||||
ini_set('memcached.sess_lock_wait_max', $this->acquiretimeout * 1000);
|
||||
// With memcached 3.x (PHP 7) we configure the max retries to make and the time to sleep between each retry.
|
||||
// There are two sleep config values, an initial and a max value.
|
||||
// After each attempt the memcached module adjusts the sleep value to be the lesser of the configured max
|
||||
// value, or 2X the previous value.
|
||||
// With default memcached.ini configs (5, 1s, 2s) the result is only 5 attempts to lock over 9 sec.
|
||||
// To mimic the behavior of the 2.2.x module so we get more attempts and much more frequently, config both
|
||||
// sleep values to the old default value of 150 msec (making it constant) and calculate number of retries
|
||||
// using the existing Moodle config $CFG->session_memcached_acquire_lock_timeout.
|
||||
// Doing this so admins configure session lock attempt timeout in familiar terms, and more straight-forward
|
||||
// to detect if lock attempt timeout has occurred in start().
|
||||
// If _min and _max values are not equal, the actual lock acquire timeout will not be the expected
|
||||
// configured value in $CFG->session_memcached_acquire_lock_timeout; this will cause session data loss when
|
||||
// failure to acquire the lock is not detected.
|
||||
ini_set('memcached.sess_lock_wait_min', $this->lockretrysleep);
|
||||
ini_set('memcached.sess_lock_wait_max', $this->lockretrysleep);
|
||||
ini_set('memcached.sess_lock_retries', (int)(($this->acquiretimeout * 1000) / $this->lockretrysleep) + 1);
|
||||
} else {
|
||||
// With memcached 2.2.x we configure max time to attempt lock, and accept default value (in memcached.ini)
|
||||
// for sleep time between each attempt (usually 150 msec), then memcached calculates the max number of
|
||||
// retries to make.
|
||||
ini_set('memcached.sess_lock_max_wait', $this->acquiretimeout);
|
||||
}
|
||||
|
||||
ini_set('memcached.sess_lock_expire', $this->lockexpire);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,6 +44,10 @@ class refresh_mod_calendar_events_task extends adhoc_task {
|
||||
* Run the task to refresh calendar events.
|
||||
*/
|
||||
public function execute() {
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
|
||||
// Specific list of plugins that need to be refreshed. If not set, then all mod plugins will be refreshed.
|
||||
$pluginstorefresh = null;
|
||||
if (isset($this->get_custom_data()->plugins)) {
|
||||
|
||||
+30
-1
@@ -2906,7 +2906,12 @@ function xmldb_main_upgrade($oldversion) {
|
||||
['hub', '%' . $DB->sql_like_escape('_' . $cleanoldhuburl)]);
|
||||
foreach ($entries as $entry) {
|
||||
$newname = substr($entry->name, 0, -strlen($cleanoldhuburl)) . $cleannewhuburl;
|
||||
$DB->update_record('config_plugins', ['id' => $entry->id, 'name' => $newname]);
|
||||
try {
|
||||
$DB->update_record('config_plugins', ['id' => $entry->id, 'name' => $newname]);
|
||||
} catch (dml_exception $e) {
|
||||
// Entry with new name already exists, remove the one with an old name.
|
||||
$DB->delete_records('config_plugins', ['id' => $entry->id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Update published courses.
|
||||
@@ -2932,5 +2937,29 @@ function xmldb_main_upgrade($oldversion) {
|
||||
upgrade_main_savepoint(true, 2017051501.09);
|
||||
}
|
||||
|
||||
if ($oldversion < 2017051502.01) {
|
||||
|
||||
// Force all messages to be reindexed.
|
||||
set_config('core_message_message_sent_lastindexrun', '0', 'core_search');
|
||||
set_config('core_message_message_received_lastindexrun', '0', 'core_search');
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2017051502.01);
|
||||
}
|
||||
|
||||
if ($oldversion < 2017051502.04) {
|
||||
|
||||
// Remove duplicate registrations.
|
||||
$newhuburl = "https://moodle.net";
|
||||
$registrations = $DB->get_records('registration_hubs', ['huburl' => $newhuburl], 'confirmed DESC, id ASC');
|
||||
if (count($registrations) > 1) {
|
||||
$reg = array_shift($registrations);
|
||||
$DB->delete_records_select('registration_hubs', 'huburl = ? AND id <> ?', [$newhuburl, $reg->id]);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2017051502.04);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -176,6 +176,8 @@ class mysqli_native_moodle_database extends moodle_database {
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
if ($rec = $result->fetch_assoc()) {
|
||||
// MySQL 8 BC: information_schema.* returns the fields in upper case.
|
||||
$rec = array_change_key_case($rec, CASE_LOWER);
|
||||
$engine = $rec['engine'];
|
||||
}
|
||||
$result->close();
|
||||
@@ -248,6 +250,8 @@ class mysqli_native_moodle_database extends moodle_database {
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
if ($rec = $result->fetch_assoc()) {
|
||||
// MySQL 8 BC: information_schema.* returns the fields in upper case.
|
||||
$rec = array_change_key_case($rec, CASE_LOWER);
|
||||
$collation = $rec['collation_name'];
|
||||
}
|
||||
$result->close();
|
||||
@@ -309,10 +313,12 @@ class mysqli_native_moodle_database extends moodle_database {
|
||||
$result = $this->mysqli->query($sql);
|
||||
$this->query_end($result);
|
||||
if ($rec = $result->fetch_assoc()) {
|
||||
// MySQL 8 BC: information_schema.* returns the fields in upper case.
|
||||
$rec = array_change_key_case($rec, CASE_LOWER);
|
||||
if (isset($table)) {
|
||||
$rowformat = $rec['row_format'];
|
||||
} else {
|
||||
$rowformat = $rec['Value'];
|
||||
$rowformat = $rec['value'];
|
||||
}
|
||||
}
|
||||
$result->close();
|
||||
@@ -691,6 +697,8 @@ class mysqli_native_moodle_database extends moodle_database {
|
||||
if ($result->num_rows > 0) {
|
||||
// standard table exists
|
||||
while ($rawcolumn = $result->fetch_assoc()) {
|
||||
// MySQL 8 BC: information_schema.* returns the fields in upper case.
|
||||
$rawcolumn = array_change_key_case($rawcolumn, CASE_LOWER);
|
||||
$info = (object)$this->get_column_info((object)$rawcolumn);
|
||||
$structure[$info->name] = new database_column_info($info);
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -956,4 +956,4 @@ Y.namespace('M.atto_media').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
});
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
|
||||
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-form-shortforms"]});
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -956,4 +956,4 @@ Y.namespace('M.atto_media').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
});
|
||||
|
||||
|
||||
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});
|
||||
}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-form-shortforms"]});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"moodle-atto_media-button": {
|
||||
"requires": [
|
||||
"moodle-editor_atto-plugin"
|
||||
"moodle-editor_atto-plugin",
|
||||
"moodle-form-shortforms"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1593,3 +1593,14 @@ function restrict_php_version(&$result, $version) {
|
||||
function restrict_php_version_71(&$result) {
|
||||
return restrict_php_version($result, '7.1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current PHP version is greater than or equal to
|
||||
* PHP version 7.2.
|
||||
*
|
||||
* @param object $result an environment_results instance
|
||||
* @return bool result of version check
|
||||
*/
|
||||
function restrict_php_version_72(&$result) {
|
||||
return restrict_php_version($result, '7.2');
|
||||
}
|
||||
|
||||
+1
-1
@@ -2369,7 +2369,7 @@ function send_stored_file($stored_file, $lifetime=null, $filter=0, $forcedownloa
|
||||
$size = 256;
|
||||
}
|
||||
$fileicon = file_file_icon($stored_file, $size);
|
||||
send_file($CFG->dirroot.'/pix/'.$fileicon.'.svg', basename($fileicon).'.svg');
|
||||
send_file($CFG->dirroot.'/pix/'.$fileicon.'.png', basename($fileicon).'.png');
|
||||
} else {
|
||||
// preview images have fixed cache lifetime and they ignore forced download
|
||||
// (they are generated by GD and therefore they are considered reasonably safe).
|
||||
|
||||
@@ -1326,7 +1326,7 @@ class file_storage {
|
||||
$this->create_file($newrecord);
|
||||
} catch (dml_exception $e) {
|
||||
if ($newfile) {
|
||||
$this->move_to_trash($newrecord->contenthash);
|
||||
$this->filesystem->remove_file($newrecord->contenthash);
|
||||
}
|
||||
throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid,
|
||||
$newrecord->filepath, $newrecord->filename, $e->debuginfo);
|
||||
@@ -1445,7 +1445,7 @@ class file_storage {
|
||||
$this->create_file($newrecord);
|
||||
} catch (dml_exception $e) {
|
||||
if ($newfile) {
|
||||
$this->move_to_trash($newrecord->contenthash);
|
||||
$this->filesystem->remove_file($newrecord->contenthash);
|
||||
}
|
||||
throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid,
|
||||
$newrecord->filepath, $newrecord->filename, $e->debuginfo);
|
||||
@@ -1580,7 +1580,7 @@ class file_storage {
|
||||
$filerecord->id = $DB->insert_record('files', $filerecord);
|
||||
} catch (dml_exception $e) {
|
||||
if (!empty($newfile)) {
|
||||
$this->move_to_trash($filerecord->contenthash);
|
||||
$this->filesystem->remove_file($filerecord->contenthash);
|
||||
}
|
||||
throw new stored_file_creation_exception($filerecord->contextid, $filerecord->component, $filerecord->filearea, $filerecord->itemid,
|
||||
$filerecord->filepath, $filerecord->filename, $e->debuginfo);
|
||||
|
||||
@@ -478,7 +478,12 @@ abstract class file_system {
|
||||
* @return resource file handle
|
||||
*/
|
||||
public function get_content_file_handle(stored_file $file, $type = stored_file::FILE_HANDLE_FOPEN) {
|
||||
$path = $this->get_remote_path_from_storedfile($file);
|
||||
if ($type === stored_file::FILE_HANDLE_GZOPEN) {
|
||||
// Local file required for gzopen.
|
||||
$path = $this->get_local_path_from_storedfile($file, true);
|
||||
} else {
|
||||
$path = $this->get_remote_path_from_storedfile($file);
|
||||
}
|
||||
|
||||
return self::get_file_handle_for_path($path, $type);
|
||||
}
|
||||
|
||||
@@ -904,8 +904,8 @@ class core_files_file_system_testcase extends advanced_testcase {
|
||||
$filecontent = 'example content';
|
||||
$file = $this->get_stored_file($filecontent);
|
||||
|
||||
$fs = $this->get_testable_mock(['get_remote_path_from_storedfile']);
|
||||
$fs->method('get_remote_path_from_storedfile')
|
||||
$fs = $this->get_testable_mock(['get_local_path_from_storedfile']);
|
||||
$fs->method('get_local_path_from_storedfile')
|
||||
->willReturn(__DIR__ . "/fixtures/test.tgz");
|
||||
|
||||
// Note: We are unable to determine the mode in which the $fh was opened.
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Provides {@link flickr_client} class.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2017 David Mudrák <david@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir.'/oauthlib.php');
|
||||
|
||||
/**
|
||||
* Simple Flickr API client implementing the features needed by Moodle
|
||||
*
|
||||
* @copyright 2017 David Mudrak <david@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class flickr_client extends oauth_helper {
|
||||
|
||||
/**
|
||||
* Base URL for Flickr OAuth 1.0 API calls.
|
||||
*/
|
||||
const OAUTH_ROOT = 'https://www.flickr.com/services/oauth';
|
||||
|
||||
/**
|
||||
* Base URL for Flickr REST API calls.
|
||||
*/
|
||||
const REST_ROOT = 'https://api.flickr.com/services/rest';
|
||||
|
||||
/**
|
||||
* Base URL for Flickr Upload API call.
|
||||
*/
|
||||
const UPLOAD_ROOT = 'https://up.flickr.com/services/upload/';
|
||||
|
||||
/**
|
||||
* Set up OAuth and initialize the client.
|
||||
*
|
||||
* The callback URL specified here will override the one specified in the
|
||||
* auth flow defined at Flickr Services.
|
||||
*
|
||||
* @param string $consumerkey
|
||||
* @param string $consumersecret
|
||||
* @param moodle_url|string $callbackurl
|
||||
*/
|
||||
public function __construct($consumerkey, $consumersecret, $callbackurl = '') {
|
||||
|
||||
parent::__construct([
|
||||
'api_root' => self::OAUTH_ROOT,
|
||||
'oauth_consumer_key' => $consumerkey,
|
||||
'oauth_consumer_secret' => $consumersecret,
|
||||
'oauth_callback' => $callbackurl,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily store the request token secret in the session.
|
||||
*
|
||||
* The request token secret is returned by the oauth request_token method.
|
||||
* It needs to be stored in the session before the user is redirected to
|
||||
* the Flickr to authorize the client. After redirecting back, this secret
|
||||
* is used for exchanging the request token with the access token.
|
||||
*
|
||||
* The identifiers help to avoid collisions between multiple calls to this
|
||||
* method from different plugins in the same session. They are used as the
|
||||
* session cache identifiers. Provide an associative array identifying the
|
||||
* particular method call. At least, the array must contain the 'caller'
|
||||
* with the caller's component name. Use additional items if needed.
|
||||
*
|
||||
* @param array $identifiers Identification of the call
|
||||
* @param string $secret
|
||||
*/
|
||||
public function set_request_token_secret(array $identifiers, $secret) {
|
||||
|
||||
if (empty($identifiers) || empty($identifiers['caller'])) {
|
||||
throw new coding_exception('Invalid call identification');
|
||||
}
|
||||
|
||||
$cache = cache::make_from_params(cache_store::MODE_SESSION, 'core', 'flickrclient', $identifiers);
|
||||
$cache->set('request_token_secret', $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns previously stored request token secret.
|
||||
*
|
||||
* See {@link self::set_request_token_secret()} for more details on the
|
||||
* $identifiers argument.
|
||||
*
|
||||
* @param array $identifiers Identification of the call
|
||||
* @return string|bool False on error, string secret otherwise.
|
||||
*/
|
||||
public function get_request_token_secret(array $identifiers) {
|
||||
|
||||
if (empty($identifiers) || empty($identifiers['caller'])) {
|
||||
throw new coding_exception('Invalid call identification');
|
||||
}
|
||||
|
||||
$cache = cache::make_from_params(cache_store::MODE_SESSION, 'core', 'flickrclient', $identifiers);
|
||||
|
||||
return $cache->get('request_token_secret');
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a Flickr API method.
|
||||
*
|
||||
* @param string $function API function name like 'flickr.photos.getSizes' or just 'photos.getSizes'
|
||||
* @param array $params Additional API call arguments.
|
||||
* @param string $method HTTP method to use (GET or POST).
|
||||
* @return object|bool Response as returned by the Flickr or false on invalid authentication
|
||||
*/
|
||||
public function call($function, array $params = [], $method = 'GET') {
|
||||
|
||||
if (strpos($function, 'flickr.') !== 0) {
|
||||
$function = 'flickr.'.$function;
|
||||
}
|
||||
|
||||
$params['method'] = $function;
|
||||
$params['format'] = 'json';
|
||||
$params['nojsoncallback'] = 1;
|
||||
|
||||
$rawresponse = $this->request($method, self::REST_ROOT, $params);
|
||||
$response = json_decode($rawresponse);
|
||||
|
||||
if (!is_object($response) || !isset($response->stat)) {
|
||||
throw new moodle_exception('flickr_api_call_failed', 'core_error', '', $rawresponse);
|
||||
}
|
||||
|
||||
if ($response->stat === 'ok') {
|
||||
return $response;
|
||||
|
||||
} else if ($response->stat === 'fail' && $response->code == 98) {
|
||||
// Authentication failure, give the caller a chance to re-authenticate.
|
||||
return false;
|
||||
|
||||
} else {
|
||||
throw new moodle_exception('flickr_api_call_failed', 'core_error', '', $response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the URL to fetch the given photo from.
|
||||
*
|
||||
* Flickr photos are distributed via farm servers staticflickr.com in
|
||||
* various sizes (resolutions). The method tries to find the source URL of
|
||||
* the photo in the highest possible resolution. Results are cached so that
|
||||
* we do not need to query the Flickr API over and over again.
|
||||
*
|
||||
* @param string $photoid Flickr photo identifier
|
||||
* @return string URL
|
||||
*/
|
||||
public function get_photo_url($photoid) {
|
||||
|
||||
$cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'core', 'flickrclient');
|
||||
|
||||
$url = $cache->get('photourl_'.$photoid);
|
||||
|
||||
if ($url === false) {
|
||||
$response = $this->call('photos.getSizes', ['photo_id' => $photoid]);
|
||||
// Sizes are returned from smallest to greatest.
|
||||
if (!empty($response->sizes->size) && is_array($response->sizes->size)) {
|
||||
while ($bestsize = array_pop($response->sizes->size)) {
|
||||
if (isset($bestsize->source)) {
|
||||
$url = $bestsize->source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($url === false) {
|
||||
throw new repository_exception('cannotdownload', 'repository');
|
||||
|
||||
} else {
|
||||
$cache->set('photourl_'.$photoid, $url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a photo from Moodle file pool to Flickr.
|
||||
*
|
||||
* Optional meta information are title, description, tags, is_public,
|
||||
* is_friend, is_family, safety_level, content_type and hidden.
|
||||
* See {@link https://www.flickr.com/services/api/upload.api.html}.
|
||||
*
|
||||
* Upload can't be asynchronous because then the query would not return the
|
||||
* photo ID which we need to add the photo to a photoset (album)
|
||||
* eventually.
|
||||
*
|
||||
* @param stored_file $photo stored in Moodle file pool
|
||||
* @param array $meta optional meta information
|
||||
* @return int|bool photo id, false on authentication failure
|
||||
*/
|
||||
public function upload(stored_file $photo, array $meta = []) {
|
||||
|
||||
$args = [
|
||||
'title' => isset($meta['title']) ? $meta['title'] : null,
|
||||
'description' => isset($meta['description']) ? $meta['description'] : null,
|
||||
'tags' => isset($meta['tags']) ? $meta['tags'] : null,
|
||||
'is_public' => isset($meta['is_public']) ? $meta['is_public'] : 0,
|
||||
'is_friend' => isset($meta['is_friend']) ? $meta['is_friend'] : 0,
|
||||
'is_family' => isset($meta['is_family']) ? $meta['is_family'] : 0,
|
||||
'safety_level' => isset($meta['safety_level']) ? $meta['safety_level'] : 1,
|
||||
'content_type' => isset($meta['content_type']) ? $meta['content_type'] : 1,
|
||||
'hidden' => isset($meta['hidden']) ? $meta['hidden'] : 2,
|
||||
];
|
||||
|
||||
$this->sign_secret = $this->consumer_secret.'&'.$this->access_token_secret;
|
||||
$params = $this->prepare_oauth_parameters(self::UPLOAD_ROOT, ['oauth_token' => $this->access_token] + $args, 'POST');
|
||||
|
||||
$params['photo'] = $photo;
|
||||
|
||||
$response = $this->http->post(self::UPLOAD_ROOT, $params);
|
||||
|
||||
if ($response) {
|
||||
$xml = simplexml_load_string($response);
|
||||
|
||||
if ((string)$xml['stat'] === 'ok') {
|
||||
return (int)$xml->photoid;
|
||||
|
||||
} else if ((string)$xml['stat'] === 'fail' && (int)$xml->err['code'] == 98) {
|
||||
// Authentication failure.
|
||||
return false;
|
||||
|
||||
} else {
|
||||
throw new moodle_exception('flickr_upload_failed', 'core_error', '',
|
||||
['code' => (int)$xml->err['code'], 'message' => (string)$xml->err['msg']]);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new moodle_exception('flickr_upload_error', 'core_error', '', null, $response);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@
|
||||
* ChangeLog:
|
||||
* 1. upload() method uses Moodle stored_file
|
||||
* 2. upload() method supports all params provided by http://www.flickr.com/services/api/upload.api.html
|
||||
* 3. auth() method shows debugging warning as this library cannot be used any
|
||||
* more for calling Flickr API that requires authentication.
|
||||
*
|
||||
* @package moodlecore
|
||||
* @subpackage 3rd-party
|
||||
@@ -240,6 +242,10 @@ class phpFlickr {
|
||||
|
||||
function auth ($perms = "write", $remember_uri = true)
|
||||
{
|
||||
|
||||
debugging('The flickrlib.php cannot be used for authenticated Flickr API calls.
|
||||
Flickr does not support their legacy auth API any more. Use the new flickrclient.php instead.');
|
||||
|
||||
// Redirects to Flickr's authentication piece if there is no valid token.
|
||||
// If remember_uri is set to false, the callback script (included) will
|
||||
// redirect to its default page.
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ class MoodleQuickForm_editor extends HTML_QuickForm_element implements templatab
|
||||
/** @var array options provided to initalize filepicker */
|
||||
protected $_options = array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 0, 'changeformat' => 0,
|
||||
'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED, 'context' => null, 'noclean' => 0, 'trusttext' => 0,
|
||||
'return_types' => 15, 'enable_filemanagement' => true);
|
||||
'return_types' => 15, 'enable_filemanagement' => true, 'autosave' => true);
|
||||
// 15 is $_options['return_types'] = FILE_INTERNAL | FILE_EXTERNAL | FILE_REFERENCE | FILE_CONTROLLED_LINK.
|
||||
|
||||
/** @var array values for editor */
|
||||
|
||||
@@ -199,8 +199,10 @@ class MoodleQuickForm_modgrade extends MoodleQuickForm_group {
|
||||
// Set a message so the user knows why they can not alter the grade type or scale.
|
||||
if ($this->currentgradetype == 'scale') {
|
||||
$gradesexistmsg = get_string('modgradecantchangegradetyporscalemsg', 'grades');
|
||||
} else {
|
||||
} else if ($this->canrescale) {
|
||||
$gradesexistmsg = get_string('modgradecantchangegradetypemsg', 'grades');
|
||||
} else {
|
||||
$gradesexistmsg = get_string('modgradecantchangegradetype', 'grades');
|
||||
}
|
||||
|
||||
$gradesexisthtml = '<div class=\'alert\'>' . $gradesexistmsg . '</div>';
|
||||
|
||||
@@ -185,7 +185,7 @@ Feature: Using the activity grade form element
|
||||
And I follow "Test forum name"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
When I expand all fieldsets
|
||||
Then I should see "Some grades have already been awarded, so the grade type cannot be changed. If you wish to change the maximum grade, you must first choose whether or not to rescale existing grades."
|
||||
Then I should see "You cannot change the type, as grades already exist for this item."
|
||||
And I set the field "Maximum grade" to "50"
|
||||
And I press "Save and display"
|
||||
And I should see "You cannot change the maximum grade when grades already exist for an activity with ratings"
|
||||
|
||||
@@ -448,4 +448,17 @@ class google_oauth extends oauth2_client {
|
||||
$this->header = array();
|
||||
$this->response = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a HTTP request, we override the parents because we do not
|
||||
* want to send accept headers (this was a change in the parent class and we want to keep the old behaviour).
|
||||
*
|
||||
* @param string $url The URL to request
|
||||
* @param array $options
|
||||
* @param mixed $acceptheader Not used.
|
||||
* @return bool
|
||||
*/
|
||||
protected function request($url, $options = array(), $acceptheader = 'application/json') {
|
||||
return parent::request($url, $options, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2448,8 +2448,8 @@ class global_navigation extends navigation_node {
|
||||
|
||||
$reporttab = $usercoursenode->add(get_string('activityreports'));
|
||||
|
||||
$reports = get_plugin_list_with_function('report', 'extend_navigation_user', 'lib.php');
|
||||
foreach ($reports as $reportfunction) {
|
||||
$reportfunctions = get_plugin_list_with_function('report', 'extend_navigation_user', 'lib.php');
|
||||
foreach ($reportfunctions as $reportfunction) {
|
||||
$reportfunction($reporttab, $user, $usercourse);
|
||||
}
|
||||
|
||||
@@ -2715,7 +2715,8 @@ class global_navigation extends navigation_node {
|
||||
// Just a link to course competency.
|
||||
$title = get_string('competencies', 'core_competency');
|
||||
$path = new moodle_url("/admin/tool/lp/coursecompetencies.php", array('courseid' => $course->id));
|
||||
$coursenode->add($title, $path, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/competencies', ''));
|
||||
$coursenode->add($title, $path, navigation_node::TYPE_SETTING, null, 'competencies',
|
||||
new pix_icon('i/competencies', ''));
|
||||
}
|
||||
if ($navoptions->grades) {
|
||||
$url = new moodle_url('/grade/report/index.php', array('id'=>$course->id));
|
||||
|
||||
+22
-16
@@ -184,9 +184,6 @@ class oauth_helper {
|
||||
$oauth_params['oauth_nonce'] = $this->get_nonce();
|
||||
$oauth_params['oauth_timestamp'] = $this->get_timestamp();
|
||||
$oauth_params['oauth_consumer_key'] = $this->consumer_key;
|
||||
if (!empty($this->oauth_callback)) {
|
||||
$oauth_params['oauth_callback'] = $this->oauth_callback->out(false);
|
||||
}
|
||||
$oauth_params['oauth_signature_method'] = 'HMAC-SHA1';
|
||||
$oauth_params['oauth_signature'] = $this->sign($http_method, $url, $oauth_params, $this->sign_secret);
|
||||
return $oauth_params;
|
||||
@@ -221,23 +218,25 @@ class oauth_helper {
|
||||
*/
|
||||
public function request_token() {
|
||||
$this->sign_secret = $this->consumer_secret.'&';
|
||||
$params = $this->prepare_oauth_parameters($this->request_token_api, array(), 'GET');
|
||||
|
||||
if (empty($this->oauth_callback)) {
|
||||
$params = [];
|
||||
} else {
|
||||
$params = ['oauth_callback' => $this->oauth_callback->out(false)];
|
||||
}
|
||||
|
||||
$params = $this->prepare_oauth_parameters($this->request_token_api, $params, 'GET');
|
||||
$content = $this->http->get($this->request_token_api, $params, $this->http_options);
|
||||
// Including:
|
||||
// oauth_token
|
||||
// oauth_token_secret
|
||||
$result = $this->parse_result($content);
|
||||
if (empty($result['oauth_token'])) {
|
||||
throw new moodle_exception('Error while requesting an oauth token');
|
||||
}
|
||||
// build oauth authrize url
|
||||
if (!empty($this->oauth_callback)) {
|
||||
// url must be rawurlencode
|
||||
$result['authorize_url'] = $this->authorize_url . '?oauth_token='.$result['oauth_token'].'&oauth_callback='.rawurlencode($this->oauth_callback->out(false));
|
||||
} else {
|
||||
// no callback
|
||||
$result['authorize_url'] = $this->authorize_url . '?oauth_token='.$result['oauth_token'];
|
||||
throw new moodle_exception('oauth1requesttoken', 'core_error', '', null, $content);
|
||||
}
|
||||
// Build oauth authorize url.
|
||||
$result['authorize_url'] = $this->authorize_url . '?oauth_token='.$result['oauth_token'];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -266,6 +265,11 @@ class oauth_helper {
|
||||
unset($params['oauth_callback']);
|
||||
$content = $this->http->post($this->access_token_api, $params, $this->http_options);
|
||||
$keys = $this->parse_result($content);
|
||||
|
||||
if (empty($keys['oauth_token']) || empty($keys['oauth_token_secret'])) {
|
||||
throw new moodle_exception('oauth1accesstoken', 'core_error', '', null, $content);
|
||||
}
|
||||
|
||||
$this->set_access_token($keys['oauth_token'], $keys['oauth_token_secret']);
|
||||
return $keys;
|
||||
}
|
||||
@@ -600,9 +604,10 @@ abstract class oauth2_client extends curl {
|
||||
*
|
||||
* @param string $url The URL to request
|
||||
* @param array $options
|
||||
* @param mixed $acceptheader mimetype (as string) or false to skip sending an accept header.
|
||||
* @return bool
|
||||
*/
|
||||
protected function request($url, $options = array()) {
|
||||
protected function request($url, $options = array(), $acceptheader = 'application/json') {
|
||||
$murl = new moodle_url($url);
|
||||
|
||||
if ($this->accesstoken) {
|
||||
@@ -614,8 +619,9 @@ abstract class oauth2_client extends curl {
|
||||
}
|
||||
}
|
||||
|
||||
// Force JSON format content in response.
|
||||
$this->setHeader('Accept: application/json');
|
||||
if ($acceptheader) {
|
||||
$this->setHeader('Accept: ' . $acceptheader);
|
||||
}
|
||||
|
||||
$response = parent::request($murl->out(false), $options);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user