mirror of
https://github.com/moodle/moodle.git
synced 2025-03-22 16:40:07 +01:00
Merge branch 'master' into install_master
This commit is contained in:
commit
2b7c85cc4e
77
admin/cli/generate_key.php
Normal file
77
admin/cli/generate_key.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Generates a secure key for the current server (presuming it does not already exist).
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
use \core\encryption;
|
||||
|
||||
define('CLI_SCRIPT', true);
|
||||
|
||||
require(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->libdir . '/clilib.php');
|
||||
|
||||
// Get cli options.
|
||||
[$options, $unrecognized] = cli_get_params(
|
||||
['help' => false, 'method' => null],
|
||||
['h' => 'help']);
|
||||
|
||||
if ($unrecognized) {
|
||||
$unrecognized = implode("\n ", $unrecognized);
|
||||
cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
|
||||
}
|
||||
|
||||
if ($options['help']) {
|
||||
echo "Generate secure key
|
||||
|
||||
This script manually creates a secure key within the secret data root folder (configured in
|
||||
config.php as \$CFG->secretdataroot). You must run it using an account with access to write
|
||||
to that folder.
|
||||
|
||||
In normal use Moodle automatically creates the key; this script is intended when setting up
|
||||
a new Moodle system, for cases where the secure folder is not on shared storage and the key
|
||||
may be manually installed on multiple servers.
|
||||
|
||||
Options:
|
||||
-h, --help Print out this help
|
||||
--method <method> Generate key for specified encryption method instead of default.
|
||||
* sodium
|
||||
* openssl-aes-256-ctr
|
||||
|
||||
Example:
|
||||
php admin/cli/generate_key.php
|
||||
";
|
||||
exit;
|
||||
}
|
||||
|
||||
$method = $options['method'];
|
||||
|
||||
if (encryption::key_exists($method)) {
|
||||
echo 'Key already exists: ' . encryption::get_key_file($method) . "\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
// Creates key with default permissions (no chmod).
|
||||
echo "Generating key...\n";
|
||||
encryption::create_key($method, false);
|
||||
|
||||
echo "\nKey created: " . encryption::get_key_file($method) . "\n\n";
|
||||
echo "If the key folder is not shared storage, then key files should be copied to all servers.\n";
|
64
admin/templates/setting_encryptedpassword.mustache
Normal file
64
admin/templates/setting_encryptedpassword.mustache
Normal file
@ -0,0 +1,64 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/admin_setting_encryptedpassword
|
||||
|
||||
Admin encrypted password template.
|
||||
|
||||
Context variables required for this template:
|
||||
* name - form element name
|
||||
* set - whether it is set or empty
|
||||
* id - element id
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "test",
|
||||
"id": "test0",
|
||||
"set": true
|
||||
}
|
||||
}}
|
||||
<div class="core_admin_encryptedpassword" data-encryptedpasswordid="{{ id }}"
|
||||
{{#novalue}}data-novalue="y"{{/novalue}}>
|
||||
{{#set}}
|
||||
<span>{{# str }} encryptedpassword_set, admin {{/ str }}</span>
|
||||
{{/set}}
|
||||
{{^set}}
|
||||
<a href="#" title="{{# str }} encryptedpassword_edit, admin {{/ str }}">
|
||||
<span>{{# str }} novalueclicktoset, form {{/ str }}</span>
|
||||
{{# pix }} t/passwordunmask-edit, core, {{# str }} passwordunmaskedithint, form {{/ str }}{{/ pix }}
|
||||
</a>
|
||||
{{/set}}
|
||||
<input style="display: none" type="password" name="{{name}}" disabled>
|
||||
{{!
|
||||
Using buttons instead of links here allows them to be connected to the label, so the button
|
||||
works if you click the label.
|
||||
}}
|
||||
{{#set}}
|
||||
<button type="button" id="{{id}}" title="{{# str }} encryptedpassword_edit, admin {{/ str }}" class="btn btn-link" data-editbutton>
|
||||
{{# pix }} t/passwordunmask-edit, core, {{/ pix }}
|
||||
</button>
|
||||
{{/set}}
|
||||
<button type="button" style="display: none" title="{{# str }} cancel {{/ str }}" class="btn btn-link" data-cancelbutton>
|
||||
<i class="icon fa fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{#js}}
|
||||
require(['core_form/encryptedpassword'], function(encryptedpassword) {
|
||||
new encryptedpassword.EncryptedPassword("{{ id }}");
|
||||
});
|
||||
{{/js}}
|
@ -89,7 +89,7 @@ class behat_admin extends behat_base {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified site settings. A table with | config | value | (optional)plugin | is expected.
|
||||
* Sets the specified site settings. A table with | config | value | (optional)plugin | (optional)encrypted | is expected.
|
||||
*
|
||||
* @Given /^the following config values are set as admin:$/
|
||||
* @param TableNode $table
|
||||
@ -103,11 +103,20 @@ class behat_admin extends behat_base {
|
||||
foreach ($data as $config => $value) {
|
||||
// Default plugin value is null.
|
||||
$plugin = null;
|
||||
$encrypted = false;
|
||||
|
||||
if (is_array($value)) {
|
||||
$plugin = $value[1];
|
||||
if (array_key_exists(2, $value)) {
|
||||
$encrypted = $value[2] === 'encrypted';
|
||||
}
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if ($encrypted) {
|
||||
$value = \core\encryption::encrypt($value);
|
||||
}
|
||||
|
||||
set_config($config, $value, $plugin);
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,18 @@ Feature: Edit capabilities
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email |
|
||||
| teacher1 | Teacher | 1 | teacher1@example.com |
|
||||
| username | firstname | lastname |
|
||||
| teacher1 | Teacher | 1 |
|
||||
| tutor | Teaching | Assistant |
|
||||
| student | Student | One |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| user | course | role |
|
||||
| teacher1 | C1 | editingteacher |
|
||||
| tutor | C1 | teacher |
|
||||
| student | C1 | student |
|
||||
|
||||
Scenario: Default system capabilities modification
|
||||
Given I log in as "admin"
|
||||
@ -60,3 +64,25 @@ Feature: Edit capabilities
|
||||
Then "mod/forum:deleteanypost" capability has "Prohibit" permission
|
||||
And "mod/forum:editanypost" capability has "Prevent" permission
|
||||
And "mod/forum:addquestion" capability has "Allow" permission
|
||||
|
||||
@javascript
|
||||
Scenario: Edit permissions escapes role names correctly
|
||||
When I am on the "C1" "Course" page logged in as "admin"
|
||||
And I navigate to "Edit settings" in current page administration
|
||||
And I set the following fields to these values:
|
||||
| Your word for 'Teacher' | Teacher >= editing |
|
||||
| Your word for 'Non-editing teacher' | Teacher < "editing" |
|
||||
| Your word for 'Student' | Studier & 'learner' |
|
||||
And I press "Save and display"
|
||||
And I navigate to course participants
|
||||
Then I should see "Teacher >= editing (Teacher)" in the "Teacher 1" "table_row"
|
||||
And I should see "Teacher < \"editing\" (Non-editing teacher)" in the "Teaching Assistant" "table_row"
|
||||
And I should see "Studier & 'learner' (Student)" in the "Student One" "table_row"
|
||||
And I navigate to "Users > Permissions" in current page administration
|
||||
And I should see "Teacher >= editing" in the "mod/forum:replypost" "table_row"
|
||||
And I should see "Teacher < \"editing\"" in the "mod/forum:replypost" "table_row"
|
||||
And I should see "Studier & 'learner'" in the "mod/forum:replypost" "table_row"
|
||||
And I follow "Prohibit"
|
||||
And "Teacher >= editing" "button" in the "Prohibit role" "dialogue" should be visible
|
||||
And "Teacher < \"editing\"" "button" in the "Prohibit role" "dialogue" should be visible
|
||||
And "Studier & 'learner'" "button" in the "Prohibit role" "dialogue" should be visible
|
||||
|
@ -28,15 +28,16 @@ Feature: Verify that keyboard steps work as expected
|
||||
And I press the shift tab key
|
||||
And the focused element is "Username" "field"
|
||||
|
||||
@javascript
|
||||
Scenario: Using the arrow keys allows me to navigate through menus
|
||||
Given the following "users" exist:
|
||||
| username | email | firstname | lastname |
|
||||
| saffronr | saffron.rutledge@example.com | Saffron | Rutledge |
|
||||
And I log in as "saffronr"
|
||||
And I click on "Saffron Rutledge" "link" in the ".usermenu" "css_element"
|
||||
When I press the up key
|
||||
Then the focused element is "Log out" "link"
|
||||
# TODO: Uncomment the following when MDL-66979 is integrated.
|
||||
# @javascript
|
||||
# Scenario: Using the arrow keys allows me to navigate through menus
|
||||
# Given the following "users" exist:
|
||||
# | username | email | firstname | lastname |
|
||||
# | saffronr | saffron.rutledge@example.com | Saffron | Rutledge |
|
||||
# And I log in as "saffronr"
|
||||
# And I click on "Saffron Rutledge" "link" in the ".usermenu" "css_element"
|
||||
# When I press the up key
|
||||
# Then the focused element is "Log out" "link"
|
||||
|
||||
@javascript
|
||||
Scenario: The escape key can be used to close a dialogue
|
||||
|
@ -1,5 +1,10 @@
|
||||
This files describes API changes in /admin/*.
|
||||
|
||||
=== 3.11 ===
|
||||
|
||||
* New admin setting admin_setting_encryptedpassword allows passwords in admin settings to be
|
||||
encrypted (with the new \core\encryption API) so that even the admin cannot read them.
|
||||
|
||||
=== 3.9 ===
|
||||
|
||||
* The following functions, previously used (exclusively) by upgrade steps are not available anymore because of the upgrade cleanup performed for this version. See MDL-65809 for more info:
|
||||
|
@ -603,8 +603,8 @@ class manager {
|
||||
*/
|
||||
public static function add_builtin_models() {
|
||||
|
||||
debugging('core_analytics\manager::add_builtin_models() has been deprecated. Core models are now automatically '.
|
||||
'updated according to their declaration in the lib/db/analytics.php file.', DEBUG_DEVELOPER);
|
||||
throw new \coding_exception('core_analytics\manager::add_builtin_models() has been removed. Core models ' .
|
||||
'are now automatically updated according to their declaration in the lib/db/analytics.php file.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,10 @@ information provided here is intended especially for developers.
|
||||
=== 3.11 ===
|
||||
* Final deprecation get_enabled_time_splitting_methods. Method has been removed. Use
|
||||
get_time_splitting_methods_for_evaluation instead.
|
||||
* Final deprecation add_builtin_models. Method has been removed. The functionality
|
||||
has been replaced with automatic update of models provided by the core moodle component.
|
||||
There is no need to call this method explicitly any more. Instead, adding new models can be achieved
|
||||
by updating the lib/db/analytics.php file and bumping the core version.
|
||||
|
||||
=== 3.8 ===
|
||||
|
||||
|
@ -727,6 +727,22 @@ $CFG->admin = 'admin';
|
||||
//
|
||||
// $CFG->maxcoursesincategory = 10000;
|
||||
//
|
||||
// Admin setting encryption
|
||||
//
|
||||
// $CFG->secretdataroot = '/var/www/my_secret_folder';
|
||||
//
|
||||
// Location to store encryption keys. By default this is $CFG->dataroot/secret; set this if
|
||||
// you want to use a different location for increased security (e.g. if too many people have access
|
||||
// to the main dataroot, or if you want to avoid using shared storage). Your web server user needs
|
||||
// read access to this location, and write access unless you manually create the keys.
|
||||
//
|
||||
// $CFG->nokeygeneration = false;
|
||||
//
|
||||
// If you change this to true then the server will give an error if keys don't exist, instead of
|
||||
// automatically generating them. This is only needed if you want to ensure that keys are consistent
|
||||
// across a cluster when not using shared storage. If you stop the server generating keys, you will
|
||||
// need to manually generate them by running 'php admin/cli/generate_key.php'.
|
||||
|
||||
//=========================================================================
|
||||
// 7. SETTINGS FOR DEVELOPMENT SERVERS - not intended for production use!!!
|
||||
//=========================================================================
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,2 +1,2 @@
|
||||
define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),recommendedTabNav:b("region","recommended-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),resourceTabNav:b("region","resources-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)"}};a.default=c;return a.default});
|
||||
define ("core_course/local/activitychooser/selectors",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b=function(a,b){return"[data-".concat(a,"=\"").concat(b,"\"]")},c={regions:{chooser:b("region","chooser-container"),getSectionChooserOptions:function getSectionChooserOptions(a){return"".concat(a," ").concat(b("region","chooser-options-container"))},chooserOption:{container:b("region","chooser-option-container"),actions:b("region","chooser-option-actions-container"),info:b("region","chooser-option-info-container")},chooserSummary:{container:b("region","chooser-option-summary-container"),content:b("region","chooser-option-summary-content-container"),header:b("region","summary-header"),actions:b("region","chooser-option-summary-actions-container")},carousel:b("region","carousel"),help:b("region","help"),modules:b("region","modules"),favouriteTabNav:b("region","favourite-tab-nav"),defaultTabNav:b("region","default-tab-nav"),activityTabNav:b("region","activity-tab-nav"),favouriteTab:b("region","favourites"),recommendedTab:b("region","recommended"),defaultTab:b("region","default"),activityTab:b("region","activity"),resourceTab:b("region","resources"),getModuleSelector:function getModuleSelector(a){return"[role=\"menuitem\"][data-modname=\"".concat(a,"\"]")},searchResults:b("region","search-results-container"),searchResultItems:b("region","search-result-items-container")},actions:{optionActions:{showSummary:b("action","show-option-summary"),manageFavourite:b("action","manage-module-favourite")},addChooser:b("action","add-chooser-option"),closeOption:b("action","close-chooser-option-summary"),hide:b("action","hide"),search:b("action","search"),clearSearch:b("action","clearsearch")},render:{favourites:b("render","favourites-area")},elements:{section:".section",sectionmodchooser:"button.section-modchooser-link",sitemenu:".block_site_main_menu",sitetopic:"div.sitetopic",tab:"a[data-toggle=\"tab\"]",activetab:"a[data-toggle=\"tab\"][aria-selected=\"true\"]",visibletabs:"a[data-toggle=\"tab\"]:not(.d-none)"}};a.default=c;return a.default});
|
||||
//# sourceMappingURL=selectors.min.js.map
|
||||
|
File diff suppressed because one or more lines are too long
@ -216,7 +216,6 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa
|
||||
const firstChooserOption = sectionChooserOptions.querySelector(selectors.regions.chooserOption.container);
|
||||
|
||||
toggleFocusableChooserOption(firstChooserOption, true);
|
||||
initTabsKeyboardNavigation(body);
|
||||
initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);
|
||||
|
||||
return body;
|
||||
@ -235,77 +234,6 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite, footerDa
|
||||
.catch();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the keyboard navigation controls for the tab list items.
|
||||
*
|
||||
* @method initTabsKeyboardNavigation
|
||||
* @param {HTMLElement} body Our modal that we are working with
|
||||
*/
|
||||
const initTabsKeyboardNavigation = (body) => {
|
||||
// Set up the tab handlers.
|
||||
const favTabNav = body.querySelector(selectors.regions.favouriteTabNav);
|
||||
const recommendedTabNav = body.querySelector(selectors.regions.recommendedTabNav);
|
||||
const defaultTabNav = body.querySelector(selectors.regions.defaultTabNav);
|
||||
const activityTabNav = body.querySelector(selectors.regions.activityTabNav);
|
||||
const resourceTabNav = body.querySelector(selectors.regions.resourceTabNav);
|
||||
const tabNavArray = [favTabNav, recommendedTabNav, defaultTabNav, activityTabNav, resourceTabNav];
|
||||
tabNavArray.forEach((element) => {
|
||||
return element.addEventListener('keydown', (e) => {
|
||||
// The first visible navigation tab link.
|
||||
const firstLink = e.target.parentElement.querySelector(selectors.elements.visibletabs);
|
||||
// The last navigation tab link. It would always be the default activities tab link.
|
||||
const lastLink = e.target.parentElement.lastElementChild;
|
||||
|
||||
if (e.keyCode === arrowRight) {
|
||||
const nextLink = e.target.nextElementSibling;
|
||||
if (nextLink === null) {
|
||||
e.target.tabIndex = -1;
|
||||
firstLink.tabIndex = 0;
|
||||
firstLink.focus();
|
||||
} else if (nextLink.classList.contains('d-none')) {
|
||||
e.target.tabIndex = -1;
|
||||
lastLink.tabIndex = 0;
|
||||
lastLink.focus();
|
||||
} else {
|
||||
e.target.tabIndex = -1;
|
||||
nextLink.tabIndex = 0;
|
||||
nextLink.focus();
|
||||
}
|
||||
}
|
||||
if (e.keyCode === arrowLeft) {
|
||||
const previousLink = e.target.previousElementSibling;
|
||||
if (previousLink === null) {
|
||||
e.target.tabIndex = -1;
|
||||
lastLink.tabIndex = 0;
|
||||
lastLink.focus();
|
||||
} else if (previousLink.classList.contains('d-none')) {
|
||||
e.target.tabIndex = -1;
|
||||
firstLink.tabIndex = 0;
|
||||
firstLink.focus();
|
||||
} else {
|
||||
e.target.tabIndex = -1;
|
||||
previousLink.tabIndex = 0;
|
||||
previousLink.focus();
|
||||
}
|
||||
}
|
||||
if (e.keyCode === home) {
|
||||
e.target.tabIndex = -1;
|
||||
firstLink.tabIndex = 0;
|
||||
firstLink.focus();
|
||||
}
|
||||
if (e.keyCode === end) {
|
||||
e.target.tabIndex = -1;
|
||||
lastLink.tabIndex = 0;
|
||||
lastLink.focus();
|
||||
}
|
||||
if (e.keyCode === space) {
|
||||
e.preventDefault();
|
||||
e.target.click();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the keyboard navigation controls for the chooser options.
|
||||
*
|
||||
|
@ -52,10 +52,8 @@ export default {
|
||||
help: getDataSelector('region', 'help'),
|
||||
modules: getDataSelector('region', 'modules'),
|
||||
favouriteTabNav: getDataSelector('region', 'favourite-tab-nav'),
|
||||
recommendedTabNav: getDataSelector('region', 'recommended-tab-nav'),
|
||||
defaultTabNav: getDataSelector('region', 'default-tab-nav'),
|
||||
activityTabNav: getDataSelector('region', 'activity-tab-nav'),
|
||||
resourceTabNav: getDataSelector('region', 'resources-tab-nav'),
|
||||
favouriteTab: getDataSelector('region', 'favourites'),
|
||||
recommendedTab: getDataSelector('region', 'recommended'),
|
||||
defaultTab: getDataSelector('region', 'default'),
|
||||
|
@ -36,7 +36,7 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_cohort/roleid',
|
||||
get_string('defaultrole', 'role'), '', $student->id, $options));
|
||||
get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
|
||||
|
||||
$options = array(
|
||||
ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'),
|
||||
|
@ -86,7 +86,11 @@ if ($ADMIN->fulltree) {
|
||||
$options = get_default_enrol_roles(context_system::instance());
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_database/defaultrole', get_string('defaultrole', 'enrol_database'), get_string('defaultrole_desc', 'enrol_database'), $student->id, $options));
|
||||
$settings->add(new admin_setting_configselect('enrol_database/defaultrole',
|
||||
get_string('defaultrole', 'enrol_database'),
|
||||
get_string('defaultrole_desc', 'enrol_database'),
|
||||
$student->id ?? null,
|
||||
$options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configcheckbox('enrol_database/ignorehiddencourses', get_string('ignorehiddencourses', 'enrol_database'), get_string('ignorehiddencourses_desc', 'enrol_database'), 0));
|
||||
|
@ -679,28 +679,34 @@ class core_enrol_external extends external_api {
|
||||
*/
|
||||
public static function get_enrolled_users_parameters() {
|
||||
return new external_function_parameters(
|
||||
array(
|
||||
[
|
||||
'courseid' => new external_value(PARAM_INT, 'course id'),
|
||||
'options' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
|
||||
'value' => new external_value(PARAM_RAW, 'option value')
|
||||
)
|
||||
]
|
||||
), 'Option names:
|
||||
* withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
|
||||
* groupid (integer) return only users in this group id. If the course has groups enabled and this param
|
||||
isn\'t defined, returns all the viewable users.
|
||||
This option requires \'moodle/site:accessallgroups\' on the course context if the
|
||||
user doesn\'t belong to the group.
|
||||
* onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
|
||||
* onlyactive (integer) return only users with active enrolments and matching time restrictions.
|
||||
This option requires \'moodle/course:enrolreview\' on the course context.
|
||||
Please note that this option can\'t
|
||||
be used together with onlysuspended (only one can be active).
|
||||
* onlysuspended (integer) return only suspended users. This option requires
|
||||
\'moodle/course:enrolreview\' on the course context. Please note that this option can\'t
|
||||
be used together with onlyactive (only one can be active).
|
||||
* userfields (\'string, string, ...\') return only the values of these user fields.
|
||||
* limitfrom (integer) sql limit from.
|
||||
* limitnumber (integer) maximum number of returned users.
|
||||
* sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
|
||||
* sortdirection (string) ASC or DESC',
|
||||
VALUE_DEFAULT, array()),
|
||||
)
|
||||
VALUE_DEFAULT, []),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@ -714,7 +720,7 @@ class core_enrol_external extends external_api {
|
||||
* }
|
||||
* @return array An array of users
|
||||
*/
|
||||
public static function get_enrolled_users($courseid, $options = array()) {
|
||||
public static function get_enrolled_users($courseid, $options = []) {
|
||||
global $CFG, $USER, $DB;
|
||||
|
||||
require_once($CFG->dirroot . '/course/lib.php');
|
||||
@ -722,67 +728,71 @@ class core_enrol_external extends external_api {
|
||||
|
||||
$params = self::validate_parameters(
|
||||
self::get_enrolled_users_parameters(),
|
||||
array(
|
||||
[
|
||||
'courseid'=>$courseid,
|
||||
'options'=>$options
|
||||
)
|
||||
]
|
||||
);
|
||||
$withcapability = '';
|
||||
$groupid = 0;
|
||||
$onlyactive = false;
|
||||
$userfields = array();
|
||||
$onlysuspended = false;
|
||||
$userfields = [];
|
||||
$limitfrom = 0;
|
||||
$limitnumber = 0;
|
||||
$sortby = 'us.id';
|
||||
$sortparams = array();
|
||||
$sortparams = [];
|
||||
$sortdirection = 'ASC';
|
||||
foreach ($options as $option) {
|
||||
switch ($option['name']) {
|
||||
case 'withcapability':
|
||||
$withcapability = $option['value'];
|
||||
break;
|
||||
case 'groupid':
|
||||
$groupid = (int)$option['value'];
|
||||
break;
|
||||
case 'onlyactive':
|
||||
$onlyactive = !empty($option['value']);
|
||||
break;
|
||||
case 'userfields':
|
||||
$thefields = explode(',', $option['value']);
|
||||
foreach ($thefields as $f) {
|
||||
$userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
|
||||
}
|
||||
break;
|
||||
case 'limitfrom' :
|
||||
$limitfrom = clean_param($option['value'], PARAM_INT);
|
||||
break;
|
||||
case 'limitnumber' :
|
||||
$limitnumber = clean_param($option['value'], PARAM_INT);
|
||||
break;
|
||||
case 'sortby':
|
||||
$sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
|
||||
if (!in_array($option['value'], $sortallowedvalues)) {
|
||||
throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
|
||||
'allowed values are: ' . implode(',', $sortallowedvalues));
|
||||
}
|
||||
if ($option['value'] == 'siteorder') {
|
||||
list($sortby, $sortparams) = users_order_by_sql('us');
|
||||
} else {
|
||||
$sortby = 'us.' . $option['value'];
|
||||
}
|
||||
break;
|
||||
case 'sortdirection':
|
||||
$sortdirection = strtoupper($option['value']);
|
||||
$directionallowedvalues = array('ASC', 'DESC');
|
||||
if (!in_array($sortdirection, $directionallowedvalues)) {
|
||||
throw new invalid_parameter_exception('Invalid value for sortdirection parameter
|
||||
case 'withcapability':
|
||||
$withcapability = $option['value'];
|
||||
break;
|
||||
case 'groupid':
|
||||
$groupid = (int)$option['value'];
|
||||
break;
|
||||
case 'onlyactive':
|
||||
$onlyactive = !empty($option['value']);
|
||||
break;
|
||||
case 'onlysuspended':
|
||||
$onlysuspended = !empty($option['value']);
|
||||
break;
|
||||
case 'userfields':
|
||||
$thefields = explode(',', $option['value']);
|
||||
foreach ($thefields as $f) {
|
||||
$userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
|
||||
}
|
||||
break;
|
||||
case 'limitfrom' :
|
||||
$limitfrom = clean_param($option['value'], PARAM_INT);
|
||||
break;
|
||||
case 'limitnumber' :
|
||||
$limitnumber = clean_param($option['value'], PARAM_INT);
|
||||
break;
|
||||
case 'sortby':
|
||||
$sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder'];
|
||||
if (!in_array($option['value'], $sortallowedvalues)) {
|
||||
throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' .
|
||||
$option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues));
|
||||
}
|
||||
if ($option['value'] == 'siteorder') {
|
||||
list($sortby, $sortparams) = users_order_by_sql('us');
|
||||
} else {
|
||||
$sortby = 'us.' . $option['value'];
|
||||
}
|
||||
break;
|
||||
case 'sortdirection':
|
||||
$sortdirection = strtoupper($option['value']);
|
||||
$directionallowedvalues = ['ASC', 'DESC'];
|
||||
if (!in_array($sortdirection, $directionallowedvalues)) {
|
||||
throw new invalid_parameter_exception('Invalid value for sortdirection parameter
|
||||
(value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
|
||||
$course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
|
||||
$coursecontext = context_course::instance($courseid, IGNORE_MISSING);
|
||||
if ($courseid == SITEID) {
|
||||
$context = context_system::instance();
|
||||
@ -809,11 +819,12 @@ class core_enrol_external extends external_api {
|
||||
require_capability('moodle/site:accessallgroups', $coursecontext);
|
||||
}
|
||||
// to overwrite this option, you need course:enrolereview permission
|
||||
if ($onlyactive) {
|
||||
if ($onlyactive || $onlysuspended) {
|
||||
require_capability('moodle/course:enrolreview', $coursecontext);
|
||||
}
|
||||
|
||||
list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
|
||||
list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
|
||||
$onlysuspended);
|
||||
$ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
|
||||
$ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
|
||||
$enrolledparams['contextlevel'] = CONTEXT_USER;
|
||||
@ -829,7 +840,7 @@ class core_enrol_external extends external_api {
|
||||
$enrolledparams = array_merge($enrolledparams, $groupparams);
|
||||
} else {
|
||||
// User doesn't belong to any group, so he can't see any user. Return an empty array.
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
}
|
||||
$sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
|
||||
@ -845,7 +856,7 @@ class core_enrol_external extends external_api {
|
||||
$enrolledparams['courseid'] = $courseid;
|
||||
|
||||
$enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
|
||||
$users = array();
|
||||
$users = [];
|
||||
foreach ($enrolledusers as $user) {
|
||||
context_helper::preload_from_record($user);
|
||||
if ($userdetails = user_get_user_details($user, $course, $userfields)) {
|
||||
@ -865,7 +876,7 @@ class core_enrol_external extends external_api {
|
||||
public static function get_enrolled_users_returns() {
|
||||
return new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'id' => new external_value(PARAM_INT, 'ID of the user'),
|
||||
'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
|
||||
'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
|
||||
@ -896,47 +907,47 @@ class core_enrol_external extends external_api {
|
||||
'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
|
||||
'customfields' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
|
||||
'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
|
||||
'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
|
||||
'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
|
||||
)
|
||||
]
|
||||
), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
|
||||
'groups' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'id' => new external_value(PARAM_INT, 'group id'),
|
||||
'name' => new external_value(PARAM_RAW, 'group name'),
|
||||
'description' => new external_value(PARAM_RAW, 'group description'),
|
||||
'descriptionformat' => new external_format_value('description'),
|
||||
)
|
||||
]
|
||||
), 'user groups', VALUE_OPTIONAL),
|
||||
'roles' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'roleid' => new external_value(PARAM_INT, 'role id'),
|
||||
'name' => new external_value(PARAM_RAW, 'role name'),
|
||||
'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
|
||||
'sortorder' => new external_value(PARAM_INT, 'role sortorder')
|
||||
)
|
||||
]
|
||||
), 'user roles', VALUE_OPTIONAL),
|
||||
'preferences' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'name' => new external_value(PARAM_RAW, 'The name of the preferences'),
|
||||
'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
|
||||
)
|
||||
]
|
||||
), 'User preferences', VALUE_OPTIONAL),
|
||||
'enrolledcourses' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
[
|
||||
'id' => new external_value(PARAM_INT, 'Id of the course'),
|
||||
'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
|
||||
'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
|
||||
)
|
||||
]
|
||||
), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_fee/roleid',
|
||||
get_string('defaultrole', 'enrol_fee'), get_string('defaultrole_desc', 'enrol_fee'), $student->id, $options));
|
||||
get_string('defaultrole', 'enrol_fee'), get_string('defaultrole_desc', 'enrol_fee'), $student->id ?? null, $options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configduration('enrol_fee/enrolperiod',
|
||||
|
@ -63,7 +63,7 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_manual/roleid',
|
||||
get_string('defaultrole', 'role'), '', $student->id, $options));
|
||||
get_string('defaultrole', 'role'), '', $student->id ?? null, $options));
|
||||
}
|
||||
|
||||
$options = array(2 => get_string('coursestart'), 3 => get_string('today'), 4 => get_string('now', 'enrol_manual'));
|
||||
|
@ -36,6 +36,6 @@ if ($ADMIN->fulltree) {
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect_with_advanced('enrol_mnet/roleid',
|
||||
get_string('defaultrole', 'role'), '',
|
||||
array('value'=>$student->id, 'adv'=>true), $options));
|
||||
array('value' => $student->id ?? null, 'adv' => true), $options));
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,10 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_paypal/roleid',
|
||||
get_string('defaultrole', 'enrol_paypal'), get_string('defaultrole_desc', 'enrol_paypal'), $student->id, $options));
|
||||
get_string('defaultrole', 'enrol_paypal'),
|
||||
get_string('defaultrole_desc', 'enrol_paypal'),
|
||||
$student->id ?? null,
|
||||
$options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configduration('enrol_paypal/enrolperiod',
|
||||
|
@ -79,7 +79,10 @@ if ($ADMIN->fulltree) {
|
||||
$student = get_archetype_roles('student');
|
||||
$student = reset($student);
|
||||
$settings->add(new admin_setting_configselect('enrol_self/roleid',
|
||||
get_string('defaultrole', 'enrol_self'), get_string('defaultrole_desc', 'enrol_self'), $student->id, $options));
|
||||
get_string('defaultrole', 'enrol_self'),
|
||||
get_string('defaultrole_desc', 'enrol_self'),
|
||||
$student->id ?? null,
|
||||
$options));
|
||||
}
|
||||
|
||||
$settings->add(new admin_setting_configduration('enrol_self/enrolperiod',
|
||||
|
@ -355,6 +355,95 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify get_enrolled_users() returned users according to their status.
|
||||
*/
|
||||
public function test_get_enrolled_users_active_suspended() {
|
||||
global $USER;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create the course and the users.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$coursecontext = context_course::instance($course->id);
|
||||
$user0 = $this->getDataGenerator()->create_user(['username' => 'user0active']);
|
||||
$user1 = $this->getDataGenerator()->create_user(['username' => 'user1active']);
|
||||
$user2 = $this->getDataGenerator()->create_user(['username' => 'user2active']);
|
||||
$user2su = $this->getDataGenerator()->create_user(['username' => 'user2suspended']); // Suspended user.
|
||||
$user3 = $this->getDataGenerator()->create_user(['username' => 'user3active']);
|
||||
$user3su = $this->getDataGenerator()->create_user(['username' => 'user3suspended']); // Suspended user.
|
||||
|
||||
// Enrol the users in the course.
|
||||
$this->getDataGenerator()->enrol_user($user0->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($user1->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
|
||||
$this->getDataGenerator()->enrol_user($user3->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($user3su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
|
||||
|
||||
// Create a role to add the allowedcaps. Users will have this role assigned.
|
||||
$roleid = $this->getDataGenerator()->create_role();
|
||||
// Allow the specified capabilities.
|
||||
assign_capability('moodle/course:enrolreview', CAP_ALLOW, $roleid, $coursecontext);
|
||||
assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $roleid, $coursecontext);
|
||||
|
||||
// Switch to the user and assign the role.
|
||||
$this->setUser($user0);
|
||||
role_assign($roleid, $USER->id, $coursecontext);
|
||||
|
||||
// Suspended users.
|
||||
$options = [
|
||||
['name' => 'onlysuspended', 'value' => true],
|
||||
['name' => 'userfields', 'value' => 'id,username']
|
||||
];
|
||||
$suspendedusers = core_enrol_external::get_enrolled_users($course->id, $options);
|
||||
|
||||
// We need to execute the return values cleaning process to simulate the web service server.
|
||||
$suspendedusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $suspendedusers);
|
||||
$this->assertCount(2, $suspendedusers);
|
||||
|
||||
foreach ($suspendedusers as $suspendeduser) {
|
||||
$this->assertStringContainsString('suspended', $suspendeduser['username']);
|
||||
}
|
||||
|
||||
// Active users.
|
||||
$options = [
|
||||
['name' => 'onlyactive', 'value' => true],
|
||||
['name' => 'userfields', 'value' => 'id,username']
|
||||
];
|
||||
$activeusers = core_enrol_external::get_enrolled_users($course->id, $options);
|
||||
|
||||
// We need to execute the return values cleaning process to simulate the web service server.
|
||||
$activeusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $activeusers);
|
||||
$this->assertCount(4, $activeusers);
|
||||
|
||||
foreach ($activeusers as $activeuser) {
|
||||
$this->assertStringContainsString('active', $activeuser['username']);
|
||||
}
|
||||
|
||||
// All enrolled users.
|
||||
$options = [
|
||||
['name' => 'userfields', 'value' => 'id,username']
|
||||
];
|
||||
$allusers = core_enrol_external::get_enrolled_users($course->id, $options);
|
||||
|
||||
// We need to execute the return values cleaning process to simulate the web service server.
|
||||
$allusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $allusers);
|
||||
$this->assertCount(6, $allusers);
|
||||
|
||||
// Active and suspended. Test exception is thrown.
|
||||
$options = [
|
||||
['name' => 'onlyactive', 'value' => true],
|
||||
['name' => 'onlysuspended', 'value' => true],
|
||||
['name' => 'userfields', 'value' => 'id,username']
|
||||
];
|
||||
$this->expectException('coding_exception');
|
||||
$message = 'Coding error detected, it must be fixed by a programmer: Both onlyactive ' .
|
||||
'and onlysuspended are set, this is probably not what you want!';
|
||||
$this->expectExceptionMessage($message);
|
||||
core_enrol_external::get_enrolled_users($course->id, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_users_courses
|
||||
*/
|
||||
|
@ -1,6 +1,10 @@
|
||||
This files describes API changes in /enrol/* - plugins,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.11 ===
|
||||
|
||||
* Added onlysuspended option to core_enrol_get_enrolled_users webservice to retrieve only suspended users.
|
||||
|
||||
=== 3.8 ===
|
||||
|
||||
* Function enrol_manual_plugin::enrol_cohort now return the number of enrolled cohort users.
|
||||
|
@ -575,6 +575,8 @@ $string['enableuserfeedback'] = 'Enable feedback about this software';
|
||||
$string['enableuserfeedback_desc'] = 'If enabled, a \'Give feedback about this software\' link is displayed in the footer for users to give feedback about the Moodle software to Moodle HQ. If the \'Next feedback reminder\' option is set, the user is also shown a reminder on the Dashboard at the specified interval. Setting \'Next feedback reminder\' to \'Never\' disables the Dashboard reminder, while leaving the \'Give feedback about this software\' link in the footer.';
|
||||
$string['enablewebservices'] = 'Enable web services';
|
||||
$string['enablewsdocumentation'] = 'Web services documentation';
|
||||
$string['encryptedpassword_set'] = '(Set and encrypted)';
|
||||
$string['encryptedpassword_edit'] = 'Enter new value';
|
||||
$string['enrolinstancedefaults'] = 'Enrolment instance defaults';
|
||||
$string['enrolinstancedefaults_desc'] = 'Default enrolment settings in new courses.';
|
||||
$string['enrolmultipleusers'] = 'Enrol the users';
|
||||
|
@ -236,6 +236,12 @@ $string['duplicaterolename'] = 'There is already a role with this name!';
|
||||
$string['duplicateroleshortname'] = 'There is already a role with this short name!';
|
||||
$string['duplicateusername'] = 'Duplicate username - skipping record';
|
||||
$string['emailfail'] = 'Emailing failed';
|
||||
$string['encryption_encryptfailed'] = 'Encryption failed';
|
||||
$string['encryption_decryptfailed'] = 'Decryption failed';
|
||||
$string['encryption_invalidkey'] = 'Invalid key';
|
||||
$string['encryption_keyalreadyexists'] = 'Key already exists';
|
||||
$string['encryption_nokey'] = 'Key not found';
|
||||
$string['encryption_wrongmethod'] = 'Data does not match a supported encryption method';
|
||||
$string['enddatebeforestartdate'] = 'The course end date must be after the start date.';
|
||||
$string['error'] = 'Error occurred';
|
||||
$string['error_question_answers_missing_in_db'] = 'Failed to find an answer matching "{$a->answer}" in the question_answers database table. This occurred while restoring the question with id {$a->filequestionid} in the backup file, which has been matched to the existing question with id {$a->dbquestionid} in the database.';
|
||||
|
@ -2724,6 +2724,58 @@ class admin_setting_configpasswordunmask_with_advanced extends admin_setting_con
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin setting class for encrypted values using secure encryption.
|
||||
*
|
||||
* @copyright 2019 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class admin_setting_encryptedpassword extends admin_setting {
|
||||
|
||||
/**
|
||||
* Constructor. Same as parent except that the default value is always an empty string.
|
||||
*
|
||||
* @param string $name Internal name used in config table
|
||||
* @param string $visiblename Name shown on form
|
||||
* @param string $description Description that appears below field
|
||||
*/
|
||||
public function __construct(string $name, string $visiblename, string $description) {
|
||||
parent::__construct($name, $visiblename, $description, '');
|
||||
}
|
||||
|
||||
public function get_setting() {
|
||||
return $this->config_read($this->name);
|
||||
}
|
||||
|
||||
public function write_setting($data) {
|
||||
$data = trim($data);
|
||||
if ($data === '') {
|
||||
// Value can really be set to nothing.
|
||||
$savedata = '';
|
||||
} else {
|
||||
// Encrypt value before saving it.
|
||||
$savedata = \core\encryption::encrypt($data);
|
||||
}
|
||||
return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
|
||||
}
|
||||
|
||||
public function output_html($data, $query='') {
|
||||
global $OUTPUT;
|
||||
|
||||
$default = $this->get_defaultsetting();
|
||||
$context = (object) [
|
||||
'id' => $this->get_id(),
|
||||
'name' => $this->get_full_name(),
|
||||
'set' => $data !== '',
|
||||
'novalue' => $this->get_setting() === null
|
||||
];
|
||||
$element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
|
||||
|
||||
return format_admin_setting($this, $this->visiblename, $element, $this->description,
|
||||
true, '', $default, $query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty setting used to allow flags (advanced) on settings that can have no sensible default.
|
||||
* Note: Only advanced makes sense right now - locked does not.
|
||||
|
2
lib/amd/build/permissionmanager.min.js
vendored
2
lib/amd/build/permissionmanager.min.js
vendored
@ -1,2 +1,2 @@
|
||||
define ("core/permissionmanager",["jquery","core/config","core/notification","core/templates","core/yui"],function(a,b,c,d,e){var f={ADDROLE:"a.allowlink, a.prohibitlink",REMOVEROLE:"a.preventlink, a.unprohibitlink",UNPROHIBIT:"a.unprohibitlink"},g=a.Event("rolesloaded"),h,j,k,l,m=null,n=function loadOverideableRoles(){var d={contextid:h,getroles:1,sesskey:b.sesskey};a.post(k+"roles/ajax.php",d,null,"json").done(function(b){try{l=b;n=function loadOverideableRoles(){a("body").trigger(g)};n()}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},o=function(b,e,f){var g={contextid:h,roleid:e,sesskey:M.cfg.sesskey,action:f,capability:b.data("name")};a.post(k+"roles/ajax.php",g,null,"json").done(function(f){var g=f;try{var h={rolename:l[e],roleid:e,adminurl:k,imageurl:M.util.image_url("t/delete","moodle")};switch(g){case"allow":h.spanclass="allowed";h.linkclass="preventlink";h.action="prevent";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prohibit":h.spanclass="forbidden";h.linkclass="unprohibitlink";h.action="unprohibit";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prevent":b.find("a[data-role-id=\""+e+"\"]").first().closest(".allowed").remove();return;case"unprohibit":b.find("a[data-role-id=\""+e+"\"]").first().closest(".forbidden").remove();return;default:return;}d.render("core/permissionmanager_role",h).done(function(c){if("allow"==g){a(c).insertBefore(b.find(".allowmore:first"))}else if("prohibit"==g){a(c).insertBefore(b.find(".prohibitmore:first"));var d=b.find(".allowedroles").first().find("a[data-role-id=\""+e+"\"]");if(d){d.first().closest(".allowed").remove()}}m.hide()}).fail(c.exception)}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},p=function(b){b.preventDefault();var g=a(b.currentTarget);a("body").one("rolesloaded",function(){e.use("moodle-core-notification-dialogue",function(){var b=g.data("action"),h=g.closest("tr.rolecap"),k={cap:h.data("humanname"),context:j},n=M.util.get_string("role"+b+"info","core_role",k);if(null===m){m=new M.core.dialogue({draggable:!0,modal:!0,closeButton:!0,width:"450px"})}m.set("headerContent",M.util.get_string("role"+b+"header","core_role"));var p,e,q=[];switch(b){case"allow":e=h.find(f.REMOVEROLE);break;case"prohibit":e=h.find(f.UNPROHIBIT);break;}for(p in l){var r="",s=e.filter("[data-role-id='"+p+"']").length;if(s){r="disabled"}var t={roleid:p,rolename:l[p],disabled:r};q.push(t)}d.render("core/permissionmanager_panelcontent",{message:n,roles:q}).done(function(c){m.set("bodyContent",c);m.show();a("div.role_buttons").on("click","input",function(c){var d=a(c.currentTarget).data("role-id");o(h,d,b)})}).fail(c.exception)})});n()},q=function(b){b.preventDefault();var d=a(b.currentTarget);a("body").one("rolesloaded",function(){var a=d.data("action"),b=d.data("role-id"),e=d.closest("tr.rolecap"),f={role:l[b],cap:e.data("humanname"),context:j};c.confirm(M.util.get_string("confirmunassigntitle","core_role"),M.util.get_string("confirmrole"+a,"core_role",f),M.util.get_string("confirmunassignyes","core_role"),M.util.get_string("confirmunassignno","core_role"),function(){o(e,b,a)})});n()};return{initialize:function initialize(b){h=b.contextid;j=b.contextname;k=b.adminurl;var c=a("body");c.on("click",f.ADDROLE,p);c.on("click",f.REMOVEROLE,q)}}});
|
||||
define ("core/permissionmanager",["jquery","core/config","core/notification","core/templates","core/yui"],function(a,b,c,d,e){var f={ADDROLE:"a.allowlink, a.prohibitlink",REMOVEROLE:"a.preventlink, a.unprohibitlink",UNPROHIBIT:"a.unprohibitlink"},g=a.Event("rolesloaded"),h,j,k,l,m=null,n=function loadOverideableRoles(){var d={contextid:h,getroles:1,sesskey:b.sesskey};a.post(k+"roles/ajax.php",d,null,"json").done(function(b){try{l=b;n=function loadOverideableRoles(){a("body").trigger(g)};n()}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},o=function(b,e,f){var g={contextid:h,roleid:e,sesskey:M.cfg.sesskey,action:f,capability:b.data("name")};a.post(k+"roles/ajax.php",g,null,"json").done(function(f){var g=f;try{var h={rolename:l[e],roleid:e,adminurl:k,imageurl:M.util.image_url("t/delete","moodle")};switch(g){case"allow":h.spanclass="allowed";h.linkclass="preventlink";h.action="prevent";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prohibit":h.spanclass="forbidden";h.linkclass="unprohibitlink";h.action="unprohibit";h.icon="t/delete";h.iconalt=M.util.get_string("deletexrole","core_role",l[e]);break;case"prevent":b.find("a[data-role-id=\""+e+"\"]").first().closest(".allowed").remove();return;case"unprohibit":b.find("a[data-role-id=\""+e+"\"]").first().closest(".forbidden").remove();return;default:return;}d.render("core/permissionmanager_role",h).done(function(c){if("allow"==g){a(c).insertBefore(b.find(".allowmore:first"))}else if("prohibit"==g){a(c).insertBefore(b.find(".prohibitmore:first"));var d=b.find(".allowedroles").first().find("a[data-role-id=\""+e+"\"]");if(d){d.first().closest(".allowed").remove()}}m.hide()}).fail(c.exception)}catch(a){c.exception(a)}}).fail(function(a,b,d){c.exception(d)})},p=function(b){b.preventDefault();var g=a(b.currentTarget);a("body").one("rolesloaded",function(){e.use("moodle-core-notification-dialogue",function(){var b=g.data("action"),h=g.closest("tr.rolecap"),k={cap:h.data("humanname"),context:j},n=M.util.get_string("role"+b+"info","core_role",k);if(null===m){m=new M.core.dialogue({draggable:!0,modal:!0,closeButton:!0,width:"450px"})}m.set("headerContent",M.util.get_string("role"+b+"header","core_role"));var p,e,q=[];switch(b){case"allow":e=h.find(f.REMOVEROLE);break;case"prohibit":e=h.find(f.UNPROHIBIT);break;}for(p in l){var r="",s=e.filter("[data-role-id='"+p+"']").length;if(s){r="disabled"}var t={roleid:p,rolename:l[p],disabled:r};q.push(t)}d.render("core/permissionmanager_panelcontent",{message:n,roles:q}).done(function(c){m.set("bodyContent",c);m.show();a("div.role_buttons").on("click","button",function(c){var d=a(c.currentTarget).data("role-id");o(h,d,b)})}).fail(c.exception)})});n()},q=function(b){b.preventDefault();var d=a(b.currentTarget);a("body").one("rolesloaded",function(){var a=d.data("action"),b=d.data("role-id"),e=d.closest("tr.rolecap"),f={role:l[b],cap:e.data("humanname"),context:j};c.confirm(M.util.get_string("confirmunassigntitle","core_role"),M.util.get_string("confirmrole"+a,"core_role",f),M.util.get_string("confirmunassignyes","core_role"),M.util.get_string("confirmunassignno","core_role"),function(){o(e,b,a)})});n()};return{initialize:function initialize(b){h=b.contextid;j=b.contextname;k=b.adminurl;var c=a("body");c.on("click",f.ADDROLE,p);c.on("click",f.REMOVEROLE,q)}}});
|
||||
//# sourceMappingURL=permissionmanager.min.js.map
|
||||
|
File diff suppressed because one or more lines are too long
@ -204,7 +204,7 @@ define(['jquery', 'core/config', 'core/notification', 'core/templates', 'core/yu
|
||||
.done(function(content) {
|
||||
panel.set('bodyContent', content);
|
||||
panel.show();
|
||||
$('div.role_buttons').on('click', 'input', function(e) {
|
||||
$('div.role_buttons').on('click', 'button', function(e) {
|
||||
var roleid = $(e.currentTarget).data('role-id');
|
||||
changePermissions(row, roleid, action);
|
||||
});
|
||||
|
@ -37,37 +37,6 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface behat_session_interface {
|
||||
/**
|
||||
* Small timeout.
|
||||
*
|
||||
* A reduced timeout for cases where self::TIMEOUT is too much
|
||||
* and a simple $this->getSession()->getPage()->find() could not
|
||||
* be enough.
|
||||
*
|
||||
* @deprecated since Moodle 3.7 MDL-64979 - please use get_reduced_timeout() instead
|
||||
* @todo MDL-64982 This will be deleted in Moodle 3.11
|
||||
* @see behat_base::get_reduced_timeout()
|
||||
*/
|
||||
const REDUCED_TIMEOUT = 2;
|
||||
|
||||
/**
|
||||
* The timeout for each Behat step (load page, wait for an element to load...).
|
||||
*
|
||||
* @deprecated since Moodle 3.7 MDL-64979 - please use get_timeout() instead
|
||||
* @todo MDL-64982 This will be deleted in Moodle 3.11
|
||||
* @see behat_base::get_timeout()
|
||||
*/
|
||||
const TIMEOUT = 6;
|
||||
|
||||
/**
|
||||
* And extended timeout for specific cases.
|
||||
*
|
||||
* @deprecated since Moodle 3.7 MDL-64979 - please use get_extended_timeout() instead
|
||||
* @todo MDL-64982 This will be deleted in Moodle 3.11
|
||||
* @see behat_base::get_extended_timeout()
|
||||
*/
|
||||
const EXTENDED_TIMEOUT = 10;
|
||||
|
||||
/**
|
||||
* The JS code to check that the page is ready.
|
||||
*
|
||||
|
318
lib/classes/encryption.php
Normal file
318
lib/classes/encryption.php
Normal file
@ -0,0 +1,318 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Class used to encrypt or decrypt data.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Class used to encrypt or decrypt data.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class encryption {
|
||||
/** @var string Encryption method: Sodium */
|
||||
const METHOD_SODIUM = 'sodium';
|
||||
/** @var string Encryption method: hand-coded OpenSSL (less safe) */
|
||||
const METHOD_OPENSSL = 'openssl-aes-256-ctr';
|
||||
|
||||
/** @var string OpenSSL cipher method */
|
||||
const OPENSSL_CIPHER = 'AES-256-CTR';
|
||||
|
||||
/**
|
||||
* Checks if Sodium is installed.
|
||||
*
|
||||
* @return bool True if the Sodium extension is available
|
||||
*/
|
||||
public static function is_sodium_installed(): bool {
|
||||
return extension_loaded('sodium');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the encryption method to use. We use the Sodium extension if it is installed, or
|
||||
* otherwise, OpenSSL.
|
||||
*
|
||||
* @return string Current encryption method
|
||||
*/
|
||||
protected static function get_encryption_method(): string {
|
||||
if (self::is_sodium_installed()) {
|
||||
return self::METHOD_SODIUM;
|
||||
} else {
|
||||
return self::METHOD_OPENSSL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a key for the server.
|
||||
*
|
||||
* @param string|null $method Encryption method (only if you want to create a non-default key)
|
||||
* @param bool $chmod If true, restricts the file access of the key
|
||||
* @throws \moodle_exception If the server already has a key, or there is an error
|
||||
*/
|
||||
public static function create_key(?string $method = null, bool $chmod = true): void {
|
||||
if ($method === null) {
|
||||
$method = self::get_encryption_method();
|
||||
}
|
||||
|
||||
if (self::key_exists($method)) {
|
||||
throw new \moodle_exception('encryption_keyalreadyexists', 'error');
|
||||
}
|
||||
|
||||
// Don't make it read-only in Behat or it will fail to clear for future runs.
|
||||
if (defined('BEHAT_SITE_RUNNING')) {
|
||||
$chmod = false;
|
||||
}
|
||||
|
||||
// Generate the key.
|
||||
switch ($method) {
|
||||
case self::METHOD_SODIUM:
|
||||
$key = sodium_crypto_secretbox_keygen();
|
||||
break;
|
||||
case self::METHOD_OPENSSL:
|
||||
$key = openssl_random_pseudo_bytes(32);
|
||||
break;
|
||||
default:
|
||||
throw new \coding_exception('Unknown method: ' . $method);
|
||||
}
|
||||
|
||||
// Store the key, making it readable only by server.
|
||||
$folder = self::get_key_folder();
|
||||
check_dir_exists($folder);
|
||||
$keyfile = self::get_key_file($method);
|
||||
file_put_contents($keyfile, $key);
|
||||
if ($chmod) {
|
||||
chmod($keyfile, 0400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the folder used to store the secret key.
|
||||
*
|
||||
* @return string Folder path
|
||||
*/
|
||||
protected static function get_key_folder(): string {
|
||||
global $CFG;
|
||||
return ($CFG->secretdataroot ?? $CFG->dataroot . '/secret') . '/key';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path used to store the secret key. The filename contains the cipher method,
|
||||
* so that if necessary to transition in future it would be possible to have multiple.
|
||||
*
|
||||
* @param string|null $method Encryption method (only if you want to get a non-default key)
|
||||
* @return string Full path to file
|
||||
*/
|
||||
public static function get_key_file(?string $method = null): string {
|
||||
if ($method === null) {
|
||||
$method = self::get_encryption_method();
|
||||
}
|
||||
|
||||
return self::get_key_folder() . '/' . $method . '.key';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is a key file.
|
||||
*
|
||||
* @param string|null $method Encryption method (only if you want to check a non-default key)
|
||||
* @return bool True if there is a key file
|
||||
*/
|
||||
public static function key_exists(?string $method = null): bool {
|
||||
if ($method === null) {
|
||||
$method = self::get_encryption_method();
|
||||
}
|
||||
|
||||
return file_exists(self::get_key_file($method));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current key, automatically creating it if there isn't one yet.
|
||||
*
|
||||
* @param string|null $method Encryption method (only if you want to get a non-default key)
|
||||
* @return string The key (binary)
|
||||
* @throws \moodle_exception If there isn't one already (and creation is disabled)
|
||||
*/
|
||||
protected static function get_key(?string $method = null): string {
|
||||
global $CFG;
|
||||
|
||||
if ($method === null) {
|
||||
$method = self::get_encryption_method();
|
||||
}
|
||||
|
||||
$keyfile = self::get_key_file($method);
|
||||
if (!file_exists($keyfile) && empty($CFG->nokeygeneration)) {
|
||||
self::create_key($method);
|
||||
}
|
||||
$result = @file_get_contents($keyfile);
|
||||
if ($result === false) {
|
||||
throw new \moodle_exception('encryption_nokey', 'error');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length in bytes of the initial values data required.
|
||||
*
|
||||
* @param string $method Crypto method
|
||||
* @return int Length in bytes
|
||||
*/
|
||||
protected static function get_iv_length(string $method): int {
|
||||
switch ($method) {
|
||||
case self::METHOD_SODIUM:
|
||||
return SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;
|
||||
case self::METHOD_OPENSSL:
|
||||
return openssl_cipher_iv_length(self::OPENSSL_CIPHER);
|
||||
default:
|
||||
throw new \coding_exception('Unknown method: ' . $method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts data using the server's key.
|
||||
*
|
||||
* Note there is a special case - the empty string is not encrypted.
|
||||
*
|
||||
* @param string $data Data to encrypt, or empty string for no data
|
||||
* @param string|null $method Encryption method (only if you want to use a non-default method)
|
||||
* @return string Encrypted data, or empty string for no data
|
||||
* @throws \moodle_exception If the key doesn't exist, or the string is too long
|
||||
*/
|
||||
public static function encrypt(string $data, ?string $method = null): string {
|
||||
if ($data === '') {
|
||||
return '';
|
||||
} else {
|
||||
if ($method === null) {
|
||||
$method = self::get_encryption_method();
|
||||
}
|
||||
|
||||
// Create IV.
|
||||
$iv = random_bytes(self::get_iv_length($method));
|
||||
|
||||
// Encrypt data.
|
||||
switch($method) {
|
||||
case self::METHOD_SODIUM:
|
||||
try {
|
||||
$encrypted = sodium_crypto_secretbox($data, $iv, self::get_key($method));
|
||||
} catch (\SodiumException $e) {
|
||||
throw new \moodle_exception('encryption_encryptfailed', 'error', '', null, $e->getMessage());
|
||||
}
|
||||
break;
|
||||
|
||||
case self::METHOD_OPENSSL:
|
||||
// This may not be a secure authenticated encryption implementation;
|
||||
// administrators should enable the Sodium extension.
|
||||
$key = self::get_key($method);
|
||||
if (strlen($key) !== 32) {
|
||||
throw new \moodle_exception('encryption_invalidkey', 'error');
|
||||
}
|
||||
$encrypted = @openssl_encrypt($data, self::OPENSSL_CIPHER, $key, OPENSSL_RAW_DATA, $iv);
|
||||
if ($encrypted === false) {
|
||||
throw new \moodle_exception('encryption_encryptfailed', 'error',
|
||||
'', null, openssl_error_string());
|
||||
}
|
||||
$hmac = hash_hmac('sha256', $iv . $encrypted, $key, true);
|
||||
$encrypted .= $hmac;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \coding_exception('Unknown method: ' . $method);
|
||||
}
|
||||
|
||||
// Encrypted data is cipher method plus IV plus encrypted data.
|
||||
return $method . ':' . base64_encode($iv . $encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts data using the server's key. The decryption works with either supported method.
|
||||
*
|
||||
* @param string $data Data to decrypt
|
||||
* @return string Decrypted data
|
||||
*/
|
||||
public static function decrypt(string $data): string {
|
||||
if ($data === '') {
|
||||
return '';
|
||||
} else {
|
||||
if (preg_match('~^(' . self::METHOD_OPENSSL . '|' . self::METHOD_SODIUM . '):~', $data, $matches)) {
|
||||
$method = $matches[1];
|
||||
} else {
|
||||
throw new \moodle_exception('encryption_wrongmethod', 'error');
|
||||
}
|
||||
$realdata = base64_decode(substr($data, strlen($method) + 1), true);
|
||||
if ($realdata === false) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, 'Invalid base64 data');
|
||||
}
|
||||
|
||||
$ivlength = self::get_iv_length($method);
|
||||
if (strlen($realdata) < $ivlength + 1) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, 'Insufficient data');
|
||||
}
|
||||
$iv = substr($realdata, 0, $ivlength);
|
||||
$encrypted = substr($realdata, $ivlength);
|
||||
|
||||
switch ($method) {
|
||||
case self::METHOD_SODIUM:
|
||||
try {
|
||||
$decrypted = sodium_crypto_secretbox_open($encrypted, $iv, self::get_key($method));
|
||||
} catch (\SodiumException $e) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, $e->getMessage());
|
||||
}
|
||||
// Sodium returns false if decryption fails because data is invalid.
|
||||
if ($decrypted === false) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, 'Integrity check failed');
|
||||
}
|
||||
break;
|
||||
|
||||
case self::METHOD_OPENSSL:
|
||||
if (strlen($encrypted) < 33) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, 'Insufficient data');
|
||||
}
|
||||
$hmac = substr($encrypted, -32);
|
||||
$encrypted = substr($encrypted, 0, -32);
|
||||
$key = self::get_key($method);
|
||||
$expectedhmac = hash_hmac('sha256', $iv . $encrypted, $key, true);
|
||||
if ($hmac !== $expectedhmac) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, 'Integrity check failed');
|
||||
}
|
||||
|
||||
$decrypted = @openssl_decrypt($encrypted, self::OPENSSL_CIPHER, $key, OPENSSL_RAW_DATA, $iv);
|
||||
if ($decrypted === false) {
|
||||
throw new \moodle_exception('encryption_decryptfailed', 'error',
|
||||
'', null, openssl_error_string());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \coding_exception('Unknown method: ' . $method);
|
||||
}
|
||||
|
||||
return $decrypted;
|
||||
}
|
||||
}
|
||||
}
|
@ -2786,11 +2786,12 @@ function xmldb_main_upgrade($oldversion) {
|
||||
if ($oldversion < 2021052500.26) {
|
||||
// Delete orphaned course_modules_completion rows; these were not deleted properly
|
||||
// by remove_course_contents function.
|
||||
$DB->delete_records_subquery('course_modules_completion', 'id', 'id',
|
||||
"SELECT cmc.id
|
||||
FROM {course_modules_completion} cmc
|
||||
LEFT JOIN {course_modules} cm ON cm.id = cmc.coursemoduleid
|
||||
WHERE cm.id IS NULL");
|
||||
$DB->delete_records_select('course_modules_completion', "
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM {course_modules} cm
|
||||
WHERE cm.id = {course_modules_completion}.coursemoduleid
|
||||
)");
|
||||
upgrade_main_savepoint(true, 2021052500.26);
|
||||
}
|
||||
|
||||
|
2
lib/form/amd/build/encryptedpassword.min.js
vendored
Normal file
2
lib/form/amd/build/encryptedpassword.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
define ("core_form/encryptedpassword",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.EncryptedPassword=void 0;var b=function(a){var b=this,c=document.querySelector("div[data-encryptedpasswordid=\""+a+"\"]");this.spanOrLink=c.querySelector("span, a");this.input=c.querySelector("input");this.editButtonOrLink=c.querySelector("button[data-editbutton], a");this.cancelButton=c.querySelector("button[data-cancelbutton]");var d=function(a){a.stopImmediatePropagation();a.preventDefault();b.startEditing(!0)};this.editButtonOrLink.addEventListener("click",d);if("A"===this.editButtonOrLink.nodeName){c.parentElement.previousElementSibling.querySelector("label").addEventListener("click",d)}this.cancelButton.addEventListener("click",function(a){a.stopImmediatePropagation();a.preventDefault();b.cancelEditing()});if("y"===c.dataset.novalue){this.startEditing(!1);this.cancelButton.style.display="none"}};a.EncryptedPassword=b;b.prototype.startEditing=function(a){this.input.style.display="inline";this.input.disabled=!1;this.spanOrLink.style.display="none";this.editButtonOrLink.style.display="none";this.cancelButton.style.display="inline";var b=this.editButtonOrLink.id;this.editButtonOrLink.removeAttribute("id");this.input.id=b;if(a){this.input.focus()}};b.prototype.cancelEditing=function(){this.input.style.display="none";this.input.value="";this.input.disabled=!0;this.spanOrLink.style.display="inline";this.editButtonOrLink.style.display="inline";this.cancelButton.style.display="none";var a=this.input.id;this.input.removeAttribute("id");this.editButtonOrLink.id=a}});
|
||||
//# sourceMappingURL=encryptedpassword.min.js.map
|
1
lib/form/amd/build/encryptedpassword.min.js.map
Normal file
1
lib/form/amd/build/encryptedpassword.min.js.map
Normal file
File diff suppressed because one or more lines are too long
104
lib/form/amd/src/encryptedpassword.js
Normal file
104
lib/form/amd/src/encryptedpassword.js
Normal file
@ -0,0 +1,104 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Encrypted password functionality.
|
||||
*
|
||||
* @module core_form/encryptedpassword
|
||||
* @package core_form
|
||||
* @class encryptedpassword
|
||||
* @copyright 2019 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructor for EncryptedPassword.
|
||||
*
|
||||
* @param {String} elementId The element to apply the encrypted password JS to
|
||||
*/
|
||||
export const EncryptedPassword = function(elementId) {
|
||||
const wrapper = document.querySelector('div[data-encryptedpasswordid="' + elementId + '"]');
|
||||
this.spanOrLink = wrapper.querySelector('span, a');
|
||||
this.input = wrapper.querySelector('input');
|
||||
this.editButtonOrLink = wrapper.querySelector('button[data-editbutton], a');
|
||||
this.cancelButton = wrapper.querySelector('button[data-cancelbutton]');
|
||||
|
||||
// Edit button action.
|
||||
var editHandler = (e) => {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
this.startEditing(true);
|
||||
};
|
||||
this.editButtonOrLink.addEventListener('click', editHandler);
|
||||
|
||||
// When it's a link, do some magic to make the label work as well.
|
||||
if (this.editButtonOrLink.nodeName === 'A') {
|
||||
wrapper.parentElement.previousElementSibling.querySelector('label').addEventListener('click', editHandler);
|
||||
}
|
||||
|
||||
// Cancel button action.
|
||||
this.cancelButton.addEventListener('click', (e) => {
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
this.cancelEditing();
|
||||
});
|
||||
|
||||
// If the value is not set yet, start editing and remove the cancel option - so that
|
||||
// it saves something in the config table and doesn't keep repeat showing it as a new
|
||||
// admin setting...
|
||||
if (wrapper.dataset.novalue === 'y') {
|
||||
this.startEditing(false);
|
||||
this.cancelButton.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts editing.
|
||||
*
|
||||
* @param {Boolean} moveFocus If true, sets focus to the edit box
|
||||
*/
|
||||
EncryptedPassword.prototype.startEditing = function(moveFocus) {
|
||||
this.input.style.display = 'inline';
|
||||
this.input.disabled = false;
|
||||
this.spanOrLink.style.display = 'none';
|
||||
this.editButtonOrLink.style.display = 'none';
|
||||
this.cancelButton.style.display = 'inline';
|
||||
|
||||
// Move the id around, which changes what happens when you click the label.
|
||||
const id = this.editButtonOrLink.id;
|
||||
this.editButtonOrLink.removeAttribute('id');
|
||||
this.input.id = id;
|
||||
|
||||
if (moveFocus) {
|
||||
this.input.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels editing.
|
||||
*/
|
||||
EncryptedPassword.prototype.cancelEditing = function() {
|
||||
this.input.style.display = 'none';
|
||||
this.input.value = '';
|
||||
this.input.disabled = true;
|
||||
this.spanOrLink.style.display = 'inline';
|
||||
this.editButtonOrLink.style.display = 'inline';
|
||||
this.cancelButton.style.display = 'none';
|
||||
|
||||
// Move the id around, which changes what happens when you click the label.
|
||||
const id = this.input.id;
|
||||
this.input.removeAttribute('id');
|
||||
this.editButtonOrLink.id = id;
|
||||
};
|
@ -28,14 +28,14 @@ class Html2Text
|
||||
/**
|
||||
* Contains the HTML content to convert.
|
||||
*
|
||||
* @type string
|
||||
* @var string $html
|
||||
*/
|
||||
protected $html;
|
||||
|
||||
/**
|
||||
* Contains the converted, formatted text.
|
||||
*
|
||||
* @type string
|
||||
* @var string $text
|
||||
*/
|
||||
protected $text;
|
||||
|
||||
@ -43,7 +43,7 @@ class Html2Text
|
||||
* List of preg* regular expression patterns to search for,
|
||||
* used in conjunction with $replace.
|
||||
*
|
||||
* @type array
|
||||
* @var array $search
|
||||
* @see $replace
|
||||
*/
|
||||
protected $search = array(
|
||||
@ -54,6 +54,7 @@ class Html2Text
|
||||
'/<style\b[^>]*>.*?<\/style>/i', // <style>s -- which strip_tags supposedly has problems with
|
||||
'/<i\b[^>]*>(.*?)<\/i>/i', // <i>
|
||||
'/<em\b[^>]*>(.*?)<\/em>/i', // <em>
|
||||
'/<ins\b[^>]*>(.*?)<\/ins>/i', // <ins>
|
||||
'/(<ul\b[^>]*>|<\/ul>)/i', // <ul> and </ul>
|
||||
'/(<ol\b[^>]*>|<\/ol>)/i', // <ol> and </ol>
|
||||
'/(<dl\b[^>]*>|<\/dl>)/i', // <dl> and </dl>
|
||||
@ -73,7 +74,7 @@ class Html2Text
|
||||
/**
|
||||
* List of pattern replacements corresponding to patterns searched.
|
||||
*
|
||||
* @type array
|
||||
* @var array $replace
|
||||
* @see $search
|
||||
*/
|
||||
protected $replace = array(
|
||||
@ -84,6 +85,7 @@ class Html2Text
|
||||
'', // <style>s -- which strip_tags supposedly has problems with
|
||||
'_\\1_', // <i>
|
||||
'_\\1_', // <em>
|
||||
'_\\1_', // <ins>
|
||||
"\n\n", // <ul> and </ul>
|
||||
"\n\n", // <ol> and </ol>
|
||||
"\n\n", // <dl> and </dl>
|
||||
@ -104,7 +106,7 @@ class Html2Text
|
||||
* List of preg* regular expression patterns to search for,
|
||||
* used in conjunction with $entReplace.
|
||||
*
|
||||
* @type array
|
||||
* @var array $entSearch
|
||||
* @see $entReplace
|
||||
*/
|
||||
protected $entSearch = array(
|
||||
@ -118,7 +120,7 @@ class Html2Text
|
||||
/**
|
||||
* List of pattern replacements corresponding to patterns searched.
|
||||
*
|
||||
* @type array
|
||||
* @var array $entReplace
|
||||
* @see $entSearch
|
||||
*/
|
||||
protected $entReplace = array(
|
||||
@ -133,7 +135,7 @@ class Html2Text
|
||||
* List of preg* regular expression patterns to search for
|
||||
* and replace using callback function.
|
||||
*
|
||||
* @type array
|
||||
* @var array $callbackSearch
|
||||
*/
|
||||
protected $callbackSearch = array(
|
||||
'/<(h)[123456]( [^>]*)?>(.*?)<\/h[123456]>/i', // h1 - h6
|
||||
@ -141,6 +143,7 @@ class Html2Text
|
||||
'/<(br)[^>]*>[ ]*/i', // <br> with leading whitespace after the newline.
|
||||
'/<(b)( [^>]*)?>(.*?)<\/b>/i', // <b>
|
||||
'/<(strong)( [^>]*)?>(.*?)<\/strong>/i', // <strong>
|
||||
'/<(del)( [^>]*)?>(.*?)<\/del>/i', // <del>
|
||||
'/<(th)( [^>]*)?>(.*?)<\/th>/i', // <th> and </th>
|
||||
'/<(a) [^>]*href=("|\')([^"\']+)\2([^>]*)>(.*?)<\/a>/i' // <a href="">
|
||||
);
|
||||
@ -149,7 +152,7 @@ class Html2Text
|
||||
* List of preg* regular expression patterns to search for in PRE body,
|
||||
* used in conjunction with $preReplace.
|
||||
*
|
||||
* @type array
|
||||
* @var array $preSearch
|
||||
* @see $preReplace
|
||||
*/
|
||||
protected $preSearch = array(
|
||||
@ -163,7 +166,7 @@ class Html2Text
|
||||
/**
|
||||
* List of pattern replacements corresponding to patterns searched for PRE body.
|
||||
*
|
||||
* @type array
|
||||
* @var array $preReplace
|
||||
* @see $preSearch
|
||||
*/
|
||||
protected $preReplace = array(
|
||||
@ -177,21 +180,21 @@ class Html2Text
|
||||
/**
|
||||
* Temporary workspace used during PRE processing.
|
||||
*
|
||||
* @type string
|
||||
* @var string $preContent
|
||||
*/
|
||||
protected $preContent = '';
|
||||
|
||||
/**
|
||||
* Contains the base URL that relative links should resolve to.
|
||||
*
|
||||
* @type string
|
||||
* @var string $baseurl
|
||||
*/
|
||||
protected $baseurl = '';
|
||||
|
||||
/**
|
||||
* Indicates whether content in the $html variable has been converted yet.
|
||||
*
|
||||
* @type boolean
|
||||
* @var boolean $converted
|
||||
* @see $html, $text
|
||||
*/
|
||||
protected $converted = false;
|
||||
@ -199,7 +202,7 @@ class Html2Text
|
||||
/**
|
||||
* Contains URL addresses from links to be rendered in plain text.
|
||||
*
|
||||
* @type array
|
||||
* @var array $linkList
|
||||
* @see buildlinkList()
|
||||
*/
|
||||
protected $linkList = array();
|
||||
@ -207,7 +210,7 @@ class Html2Text
|
||||
/**
|
||||
* Various configuration options (able to be set in the constructor)
|
||||
*
|
||||
* @type array
|
||||
* @var array $options
|
||||
*/
|
||||
protected $options = array(
|
||||
'do_links' => 'inline', // 'none'
|
||||
@ -281,7 +284,7 @@ class Html2Text
|
||||
/**
|
||||
* Returns the text, converted from HTML.
|
||||
*
|
||||
* @return string
|
||||
* @return string Plain text
|
||||
*/
|
||||
public function getText()
|
||||
{
|
||||
@ -414,7 +417,7 @@ class Html2Text
|
||||
}
|
||||
|
||||
// Ignored link types
|
||||
if (preg_match('!^(javascript:|mailto:|#)!i', $link)) {
|
||||
if (preg_match('!^(javascript:|mailto:|#)!i', html_entity_decode($link))) {
|
||||
return $display;
|
||||
}
|
||||
|
||||
@ -450,6 +453,11 @@ class Html2Text
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for PRE body conversion.
|
||||
*
|
||||
* @param string &$text HTML content
|
||||
*/
|
||||
protected function convertPre(&$text)
|
||||
{
|
||||
// get the content of PRE element
|
||||
@ -486,7 +494,7 @@ class Html2Text
|
||||
/**
|
||||
* Helper function for BLOCKQUOTE body conversion.
|
||||
*
|
||||
* @param string $text HTML content
|
||||
* @param string &$text HTML content
|
||||
*/
|
||||
protected function convertBlockquotes(&$text)
|
||||
{
|
||||
@ -563,6 +571,8 @@ class Html2Text
|
||||
case 'b':
|
||||
case 'strong':
|
||||
return $this->toupper($matches[3]);
|
||||
case 'del':
|
||||
return $this->tostrike($matches[3]);
|
||||
case 'th':
|
||||
return $this->toupper("\t\t" . $matches[3] . "\n");
|
||||
case 'h':
|
||||
@ -628,4 +638,21 @@ class Html2Text
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for DEL conversion.
|
||||
*
|
||||
* @param string $text HTML content
|
||||
* @return string Converted text
|
||||
*/
|
||||
protected function tostrike($str)
|
||||
{
|
||||
$rtn = '';
|
||||
for ($i = 0; $i < mb_strlen($str); $i++) {
|
||||
$chr = mb_substr($str, $i, 1);
|
||||
$combiningChr = chr(0xC0 | 0x336 >> 6). chr(0x80 | 0x336 & 0x3F);
|
||||
$rtn .= $chr . $combiningChr;
|
||||
}
|
||||
return $rtn;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
Description of Html2Text library import into Moodle
|
||||
Description of Html2Text v4.3.1 library import into Moodle
|
||||
|
||||
Please note that we override some mb_* functions in Html2Text's namespace at
|
||||
run time. Until Html2Text adds some sort of fallback for the mb_* functions
|
||||
@ -7,7 +7,8 @@ running PHP without mbstring don't see nasty undefined function errors.
|
||||
|
||||
Instructions
|
||||
------------
|
||||
1. Clone https://github.com/mtibben/html2text.git into an unrelated directory
|
||||
2. Copy /path/to/html2text/src/Html2Text.php to lib/html2text/
|
||||
1. Download the latest release of Html2Text from https://github.com/mtibben/html2text/releases/
|
||||
2. Extract the contents of the release archive into a directory.
|
||||
3. Copy src/Html2Text.php to lib/html2text/
|
||||
|
||||
Imported from: https://github.com/mtibben/html2text.git
|
||||
Imported from: https://github.com/mtibben/html2text/releases/
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2007, Ryan Parman and Geoffrey Sneddon.
|
||||
Copyright (c) 2004-2007, Ryan Parman and Sam Sneddon.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
|
@ -11,9 +11,7 @@ compatibility and standards compliance][what_is].
|
||||
|
||||
Requirements
|
||||
------------
|
||||
* PHP 5.3.0+ (5.3.6+ recommended since SimplePie 1.4.2)
|
||||
* Support for PHP 5.2 stopped in branch `one-dot-three`
|
||||
* Support for PHP 4 stopped in branch `one-dot-two`
|
||||
* PHP 5.6+ (Required since SimplePie 1.5.3)
|
||||
* libxml2 (certain 2.7.x releases are too buggy for words, and will crash)
|
||||
* One of iconv, mbstring or intl extensions
|
||||
* cURL or fsockopen()
|
||||
@ -89,14 +87,14 @@ Authors and contributors
|
||||
### Alumni
|
||||
* [Ryan McCue][] (developer, support)
|
||||
* [Ryan Parman][] (Creator, developer, evangelism, support)
|
||||
* [Geoffrey Sneddon][] (Lead developer)
|
||||
* [Sam Sneddon][] (Lead developer)
|
||||
* [Michael Shipley][] (Submitter of patches, support)
|
||||
* [Steve Minutillo][] (Submitter of patches)
|
||||
|
||||
[Malcolm Blaney]: https://unicyclic.com/mal
|
||||
[Malcolm Blaney]: https://mblaney.xyz
|
||||
[Ryan McCue]: http://ryanmccue.info
|
||||
[Ryan Parman]: http://ryanparman.com
|
||||
[Geoffrey Sneddon]: http://gsnedders.com
|
||||
[Sam Sneddon]: https://gsnedders.com
|
||||
[Michael Shipley]: http://michaelpshipley.com
|
||||
[Steve Minutillo]: http://minutillo.com/steve/
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2017, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2017, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,10 +33,10 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @version 1.5.3
|
||||
* @copyright 2004-2017 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @version 1.5.6
|
||||
* @copyright 2004-2017 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -50,7 +50,7 @@ define('SIMPLEPIE_NAME', 'SimplePie');
|
||||
/**
|
||||
* SimplePie Version
|
||||
*/
|
||||
define('SIMPLEPIE_VERSION', '1.5.3');
|
||||
define('SIMPLEPIE_VERSION', '1.5.6');
|
||||
|
||||
/**
|
||||
* SimplePie Build
|
||||
@ -665,9 +665,9 @@ class SimplePie
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, '5.3', '<'))
|
||||
if (version_compare(PHP_VERSION, '5.6', '<'))
|
||||
{
|
||||
trigger_error('Please upgrade to PHP 5.3 or newer.');
|
||||
trigger_error('Please upgrade to PHP 5.6 or newer.');
|
||||
die();
|
||||
}
|
||||
|
||||
@ -706,7 +706,7 @@ class SimplePie
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
|
||||
if (!gc_enabled())
|
||||
{
|
||||
if (!empty($this->data['items']))
|
||||
{
|
||||
@ -1251,8 +1251,8 @@ class SimplePie
|
||||
/**
|
||||
* Set the handler to enable the display of cached images.
|
||||
*
|
||||
* @param str $page Web-accessible path to the handler_image.php file.
|
||||
* @param str $qs The query string that the value should be passed to.
|
||||
* @param string $page Web-accessible path to the handler_image.php file.
|
||||
* @param string $qs The query string that the value should be passed to.
|
||||
*/
|
||||
public function set_image_handler($page = false, $qs = 'i')
|
||||
{
|
||||
@ -1373,7 +1373,8 @@ class SimplePie
|
||||
// Decide whether to enable caching
|
||||
if ($this->cache && $parsed_feed_url['scheme'] !== '')
|
||||
{
|
||||
$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $this->feed_url), 'spc'));
|
||||
$url = $this->feed_url . ($this->force_feed ? '#force_feed' : '');
|
||||
$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $url), 'spc'));
|
||||
}
|
||||
|
||||
// Fetch the data via SimplePie_File into $this->raw_data
|
||||
@ -1712,8 +1713,8 @@ class SimplePie
|
||||
}
|
||||
$cache = $this->registry->call('Cache', 'get_handler', array($this->cache_location, call_user_func($this->cache_name_function, $file->url), 'spc'));
|
||||
}
|
||||
$this->feed_url = $file->url;
|
||||
}
|
||||
$this->feed_url = $file->url;
|
||||
$locate = null;
|
||||
}
|
||||
|
||||
@ -1911,7 +1912,8 @@ class SimplePie
|
||||
*
|
||||
* When the 'permanent' mode is disabled (default),
|
||||
* may or may not be different from the URL passed to {@see set_feed_url()},
|
||||
* depending on whether auto-discovery was used.
|
||||
* depending on whether auto-discovery was used, and whether there were
|
||||
* any redirects along the way.
|
||||
*
|
||||
* @since Preview Release (previously called `get_feed_url()` since SimplePie 0.8.)
|
||||
* @todo Support <itunes:new-feed-url>
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -150,7 +150,7 @@ class SimplePie_Content_Type_Sniffer
|
||||
}
|
||||
elseif (preg_match('/[\x00-\x08\x0E-\x1A\x1C-\x1F]/', $this->file->body))
|
||||
{
|
||||
return 'application/octect-stream';
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
|
||||
return 'text/plain';
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -109,11 +109,6 @@ class SimplePie_File
|
||||
curl_setopt($fp, CURLOPT_REFERER, $url);
|
||||
curl_setopt($fp, CURLOPT_USERAGENT, $useragent);
|
||||
curl_setopt($fp, CURLOPT_HTTPHEADER, $headers2);
|
||||
if (!ini_get('open_basedir') && !ini_get('safe_mode') && version_compare(SimplePie_Misc::get_curl_version(), '7.15.2', '>='))
|
||||
{
|
||||
curl_setopt($fp, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($fp, CURLOPT_MAXREDIRS, $redirects);
|
||||
}
|
||||
foreach ($curl_options as $curl_param => $curl_value) {
|
||||
curl_setopt($fp, $curl_param, $curl_value);
|
||||
}
|
||||
@ -148,7 +143,7 @@ class SimplePie_File
|
||||
$this->redirects++;
|
||||
$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
|
||||
$previousStatusCode = $this->status_code;
|
||||
$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
|
||||
$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
|
||||
$this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
|
||||
return;
|
||||
}
|
||||
@ -233,7 +228,7 @@ class SimplePie_File
|
||||
$this->redirects++;
|
||||
$location = SimplePie_Misc::absolutize_url($this->headers['location'], $url);
|
||||
$previousStatusCode = $this->status_code;
|
||||
$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen);
|
||||
$this->__construct($location, $timeout, $redirects, $headers, $useragent, $force_fsockopen, $curl_options);
|
||||
$this->permanent_url = ($previousStatusCode == 301) ? $location : $url;
|
||||
return;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -46,10 +46,10 @@
|
||||
*
|
||||
* @package SimplePie
|
||||
* @subpackage HTTP
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Steve Minutillo
|
||||
* @author Ryan McCue
|
||||
* @copyright 2007-2012 Geoffrey Sneddon, Steve Minutillo, Ryan McCue
|
||||
* @copyright 2007-2012 Sam Sneddon, Steve Minutillo, Ryan McCue
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php
|
||||
*/
|
||||
class SimplePie_IRI
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -121,7 +121,7 @@ class SimplePie_Item
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ((version_compare(PHP_VERSION, '5.3', '<') || !gc_enabled()) && !ini_get('zend.ze1_compatibility_mode'))
|
||||
if (!gc_enabled())
|
||||
{
|
||||
unset($this->feed);
|
||||
}
|
||||
@ -958,7 +958,7 @@ class SimplePie_Item
|
||||
public function get_link($key = 0, $rel = 'alternate')
|
||||
{
|
||||
$links = $this->get_links($rel);
|
||||
if ($links[$key] !== null)
|
||||
if ($links && $links[$key] !== null)
|
||||
{
|
||||
return $links[$key];
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -94,7 +94,7 @@ class SimplePie_Locator
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working)
|
||||
public function find($type = SIMPLEPIE_LOCATOR_ALL, &$working = null)
|
||||
{
|
||||
if ($this->is_feed($this->file))
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -364,11 +364,12 @@ class SimplePie_Misc
|
||||
}
|
||||
|
||||
// Check that the encoding is supported
|
||||
if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
|
||||
if (!in_array($input, mb_list_encodings()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!in_array($input, mb_list_encodings()))
|
||||
|
||||
if (@mb_convert_encoding("\x80", 'UTF-16BE', $input) === "\x00\x80")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -53,7 +53,7 @@
|
||||
* @author Alexander Merz <alexander.merz@web.de>
|
||||
* @author elfrink at introweb dot nl
|
||||
* @author Josh Peck <jmp at joshpeck dot org>
|
||||
* @author Geoffrey Sneddon <geoffers@gmail.com>
|
||||
* @author Sam Sneddon <geoffers@gmail.com>
|
||||
*/
|
||||
class SimplePie_Net_IPv6
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -98,12 +98,20 @@ class SimplePie_Parse_Date
|
||||
'dimanche' => 7,
|
||||
// German
|
||||
'montag' => 1,
|
||||
'mo' => 1,
|
||||
'dienstag' => 2,
|
||||
'di' => 2,
|
||||
'mittwoch' => 3,
|
||||
'mi' => 3,
|
||||
'donnerstag' => 4,
|
||||
'do' => 4,
|
||||
'freitag' => 5,
|
||||
'fr' => 5,
|
||||
'samstag' => 6,
|
||||
'sa' => 6,
|
||||
'sonnabend' => 6,
|
||||
// AFAIK no short form for sonnabend
|
||||
'so' => 7,
|
||||
'sonntag' => 7,
|
||||
// Italian
|
||||
'lunedì' => 1,
|
||||
@ -215,17 +223,28 @@ class SimplePie_Parse_Date
|
||||
'décembre' => 12,
|
||||
// German
|
||||
'januar' => 1,
|
||||
'jan' => 1,
|
||||
'februar' => 2,
|
||||
'feb' => 2,
|
||||
'märz' => 3,
|
||||
'mär' => 3,
|
||||
'april' => 4,
|
||||
'mai' => 5,
|
||||
'apr' => 4,
|
||||
'mai' => 5, // no short form for may
|
||||
'juni' => 6,
|
||||
'jun' => 6,
|
||||
'juli' => 7,
|
||||
'jul' => 7,
|
||||
'august' => 8,
|
||||
'aug' => 8,
|
||||
'september' => 9,
|
||||
'sep' => 9,
|
||||
'oktober' => 10,
|
||||
'okt' => 10,
|
||||
'november' => 11,
|
||||
'nov' => 11,
|
||||
'dezember' => 12,
|
||||
'dez' => 12,
|
||||
// Italian
|
||||
'gennaio' => 1,
|
||||
'febbraio' => 2,
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
@ -365,14 +365,7 @@ class SimplePie_Sanitize
|
||||
// Get content node
|
||||
$div = $document->getElementsByTagName('body')->item(0)->firstChild;
|
||||
// Finally, convert to a HTML string
|
||||
if (version_compare(PHP_VERSION, '5.3.6', '>='))
|
||||
{
|
||||
$data = trim($document->saveHTML($div));
|
||||
}
|
||||
else
|
||||
{
|
||||
$data = trim($document->saveXML($div));
|
||||
}
|
||||
$data = trim($document->saveHTML($div));
|
||||
|
||||
if ($this->remove_div)
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -5,7 +5,7 @@
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors
|
||||
* Copyright (c) 2004-2016, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
@ -33,9 +33,9 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Geoffrey Sneddon, Ryan McCue
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Geoffrey Sneddon
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
|
@ -1,10 +1,10 @@
|
||||
Description of SimplePie v1.5.3 library import into Moodle
|
||||
Description of SimplePie v1.5.6 library import into Moodle
|
||||
|
||||
Obtained from https://github.com/simplepie/simplepie/releases/
|
||||
|
||||
To upgrade this library:
|
||||
1. Download the latest release of SimplePie from https://github.com/simplepie/simplepie/releases/
|
||||
2. Remove everything inside lib/simplepie/ directory except README_MOODLE.txt (this file) and moodle_simplepie.php.
|
||||
2. Remove everything inside lib/simplepie/ directory except readme_moodle.txt (this file) and moodle_simplepie.php.
|
||||
3. Extract the contents of the release archive into a directory.
|
||||
4. Move the following files/directories from the extracted directory into lib/simplepie:
|
||||
- library/
|
||||
@ -13,6 +13,7 @@ To upgrade this library:
|
||||
- README.markdown
|
||||
5. That should leave you with just the following. Do not move them. If there is any difference, check if they also need to be moved and update this doc:
|
||||
- idn (This is a third-party library that SimplePie can optionally use. We don't use this in Moodle)
|
||||
- CHANGELOG.md
|
||||
- composer.json
|
||||
- db.sql
|
||||
|
||||
|
@ -41,7 +41,7 @@ use Iterator;
|
||||
*/
|
||||
class filter implements Countable, Iterator, JsonSerializable {
|
||||
|
||||
/** @var in The default filter type (ANY) */
|
||||
/** @var int The default filter type (ANY) */
|
||||
const JOINTYPE_DEFAULT = 1;
|
||||
|
||||
/** @var int None of the following match */
|
||||
|
@ -40,8 +40,8 @@ use moodle_exception;
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class filterset implements JsonSerializable {
|
||||
/** @var in The default filter type (ANY) */
|
||||
const JOINTYPE_DEFAULT = 1;
|
||||
/** @var int The default filter type (ALL) */
|
||||
const JOINTYPE_DEFAULT = 2;
|
||||
|
||||
/** @var int None of the following match */
|
||||
const JOINTYPE_NONE = 0;
|
||||
@ -53,7 +53,7 @@ abstract class filterset implements JsonSerializable {
|
||||
const JOINTYPE_ALL = 2;
|
||||
|
||||
/** @var int The join type currently in use */
|
||||
protected $jointype = self::JOINTYPE_DEFAULT;
|
||||
protected $jointype = null;
|
||||
|
||||
/** @var array The list of combined filter types */
|
||||
protected $filtertypes = null;
|
||||
@ -90,6 +90,9 @@ abstract class filterset implements JsonSerializable {
|
||||
* @return int
|
||||
*/
|
||||
public function get_join_type(): int {
|
||||
if ($this->jointype === null) {
|
||||
$this->jointype = self::JOINTYPE_DEFAULT;
|
||||
}
|
||||
return $this->jointype;
|
||||
}
|
||||
|
||||
|
@ -27,17 +27,18 @@
|
||||
|
||||
Context variables required for this template:
|
||||
* confirmation Confirmation text
|
||||
* roles array of role details
|
||||
* roles array of role details. Note: in this array, rolename must have been
|
||||
prepared for output with format_string, or more likely one of the role API functions like role_fix_names.
|
||||
|
||||
Example context (json):
|
||||
{ "message": "Do you really want to remove Non-editing teacher from the list of allowed roles for capability View added and updated modules in recent activity block?",
|
||||
"roles": [{"roleid": 1, "rolename": "manager", "disabled":"disabled"}]}
|
||||
"roles": [{"roleid": 1, "rolename": "Manager", "disabled": "disabled"}]}
|
||||
}}
|
||||
<div class="popup_content" style="text-align:center;">
|
||||
{{message}} <hr/>
|
||||
<div class="role_buttons">
|
||||
{{#roles}}
|
||||
<input type="button" value="{{rolename}}" class="btn btn-secondary mb-1" data-role-id="{{roleid}}" {{disabled}}/>
|
||||
<button type="button" class="btn btn-secondary mb-1" data-role-id="{{roleid}}" {{disabled}}>{{{rolename}}}</button>
|
||||
{{/roles}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -27,7 +27,8 @@
|
||||
* action
|
||||
|
||||
Context variables required for this template:
|
||||
* rolename Name of the role rendered
|
||||
* rolename Name of the role rendered - must have been prepared for output with format_string,
|
||||
or more likely one of the role API functions like role_fix_names.
|
||||
* roleid Id of the role
|
||||
* action WEhich action is done on click
|
||||
* spanclass class attribute of span
|
||||
@ -43,7 +44,7 @@
|
||||
"linkclass": "preventlink",
|
||||
"adminurl" : "http://localhost/moodle/admin/"}
|
||||
}}
|
||||
<span style="display:inline-block;" class="{{spanclass}}"> {{rolename}}
|
||||
<span style="display:inline-block;" class="{{spanclass}}"> {{{rolename}}}
|
||||
<a href="{{adminurl}}roles/permissions.php" class="{{linkclass}}" data-role-id="{{roleid}}" data-action="{{action}}">
|
||||
{{#icon}}
|
||||
{{#pix}}{{icon}}, core, {{iconalt}}{{/pix}}
|
||||
|
@ -40,7 +40,7 @@
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<!-- First the top most node and immediate children -->
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#link{{node.key}}" data-toggle="tab" role="tab">{{node.text}}</a>
|
||||
<a class="nav-link active" href="#link{{node.key}}" data-toggle="tab" role="tab" aria-selected="true">{{node.text}}</a>
|
||||
</li>
|
||||
<!-- Now the first level children with sub nodes -->
|
||||
{{#node.children}}
|
||||
@ -48,7 +48,7 @@
|
||||
{{#display}}
|
||||
{{^is_short_branch}}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#link{{key}}" data-toggle="tab" role="tab">{{text}}</a>
|
||||
<a class="nav-link" href="#link{{key}}" data-toggle="tab" role="tab" aria-selected="false" tabindex="-1">{{text}}</a>
|
||||
</li>
|
||||
{{/is_short_branch}}
|
||||
{{/display}}
|
||||
|
265
lib/tests/encryption_test.php
Normal file
265
lib/tests/encryption_test.php
Normal file
@ -0,0 +1,265 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Test encryption.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Test encryption.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class encryption_testcase extends \basic_testcase {
|
||||
|
||||
/**
|
||||
* Clear junk created by tests.
|
||||
*/
|
||||
protected function tearDown(): void {
|
||||
global $CFG;
|
||||
$keyfile = encryption::get_key_file(encryption::METHOD_OPENSSL);
|
||||
if (file_exists($keyfile)) {
|
||||
chmod($keyfile, 0700);
|
||||
}
|
||||
$keyfile = encryption::get_key_file(encryption::METHOD_SODIUM);
|
||||
if (file_exists($keyfile)) {
|
||||
chmod($keyfile, 0700);
|
||||
}
|
||||
remove_dir($CFG->dataroot . '/secret');
|
||||
unset($CFG->nokeygeneration);
|
||||
}
|
||||
|
||||
protected function setUp(): void {
|
||||
$this->tearDown();
|
||||
|
||||
require_once(__DIR__ . '/fixtures/testable_encryption.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using Sodium need to check the extension is available.
|
||||
*
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
protected function require_sodium(string $method) {
|
||||
if ($method == encryption::METHOD_SODIUM) {
|
||||
if (!encryption::is_sodium_installed()) {
|
||||
$this->markTestSkipped('Sodium not installed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Many of the tests work with both encryption methods.
|
||||
*
|
||||
* @return array[] Array of method options for test
|
||||
*/
|
||||
public function encryption_method_provider(): array {
|
||||
return ['Sodium' => [encryption::METHOD_SODIUM], 'OpenSSL' => [encryption::METHOD_OPENSSL]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the create_keys and get_key functions.
|
||||
*
|
||||
* @param string $method Encryption method
|
||||
* @dataProvider encryption_method_provider
|
||||
*/
|
||||
public function test_create_key(string $method): void {
|
||||
$this->require_sodium($method);
|
||||
encryption::create_key($method);
|
||||
$key = testable_encryption::get_key($method);
|
||||
|
||||
// Conveniently, both encryption methods have the same key length.
|
||||
$this->assertEquals(32, strlen($key));
|
||||
|
||||
$this->expectExceptionMessage('Key already exists');
|
||||
encryption::create_key($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests encryption and decryption with empty strings.
|
||||
*
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public function test_encrypt_and_decrypt_empty(): void {
|
||||
$this->assertEquals('', encryption::encrypt(''));
|
||||
$this->assertEquals('', encryption::decrypt(''));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests encryption when the keys weren't created yet.
|
||||
*
|
||||
* @param string $method Encryption method
|
||||
* @dataProvider encryption_method_provider
|
||||
*/
|
||||
public function test_encrypt_nokeys(string $method): void {
|
||||
global $CFG;
|
||||
$this->require_sodium($method);
|
||||
|
||||
// Prevent automatic generation of keys.
|
||||
$CFG->nokeygeneration = true;
|
||||
$this->expectExceptionMessage('Key not found');
|
||||
encryption::encrypt('frogs', $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests decryption when the data has a different encryption method
|
||||
*/
|
||||
public function test_decrypt_wrongmethod(): void {
|
||||
$this->expectExceptionMessage('Data does not match a supported encryption method');
|
||||
encryption::decrypt('FAKE-CIPHER-METHOD:xx');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests decryption when not enough data is supplied to get the IV and some data.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
public function test_decrypt_tooshort(string $method): void {
|
||||
$this->require_sodium($method);
|
||||
|
||||
$this->expectExceptionMessage('Insufficient data');
|
||||
switch ($method) {
|
||||
case encryption::METHOD_OPENSSL:
|
||||
// It needs min 49 bytes (16 bytes IV + 32 bytes HMAC + 1 byte data).
|
||||
$justtooshort = '0123456789abcdef0123456789abcdef0123456789abcdef';
|
||||
break;
|
||||
case encryption::METHOD_SODIUM:
|
||||
// Sodium needs 25 bytes at least as far as our code is concerned (24 bytes IV + 1
|
||||
// byte data); it splits out any authentication hashes itself.
|
||||
$justtooshort = '0123456789abcdef01234567';
|
||||
break;
|
||||
}
|
||||
|
||||
encryption::decrypt($method . ':' .base64_encode($justtooshort));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests decryption when data is not valid base64.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
public function test_decrypt_notbase64(string $method): void {
|
||||
$this->require_sodium($method);
|
||||
|
||||
$this->expectExceptionMessage('Invalid base64 data');
|
||||
encryption::decrypt($method . ':' . chr(160));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests decryption when the keys weren't created yet.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
public function test_decrypt_nokeys(string $method): void {
|
||||
global $CFG;
|
||||
$this->require_sodium($method);
|
||||
|
||||
// Prevent automatic generation of keys.
|
||||
$CFG->nokeygeneration = true;
|
||||
$this->expectExceptionMessage('Key not found');
|
||||
encryption::decrypt($method . ':' . base64_encode(
|
||||
'0123456789abcdef0123456789abcdef0123456789abcdef0'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test automatic generation of keys when needed.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
public function test_auto_key_generation(string $method): void {
|
||||
$this->require_sodium($method);
|
||||
|
||||
// Allow automatic generation (default).
|
||||
$encrypted = encryption::encrypt('frogs', $method);
|
||||
$this->assertEquals('frogs', encryption::decrypt($encrypted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that invalid key causes failures.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
public function test_invalid_key(string $method): void {
|
||||
global $CFG;
|
||||
$this->require_sodium($method);
|
||||
|
||||
// Set the key to something bogus.
|
||||
$folder = $CFG->dataroot . '/secret/key';
|
||||
check_dir_exists($folder);
|
||||
file_put_contents(encryption::get_key_file($method), 'silly');
|
||||
|
||||
switch ($method) {
|
||||
case encryption::METHOD_SODIUM:
|
||||
$this->expectExceptionMessage('key size should be');
|
||||
break;
|
||||
|
||||
case encryption::METHOD_OPENSSL:
|
||||
$this->expectExceptionMessage('Invalid key');
|
||||
break;
|
||||
}
|
||||
encryption::encrypt('frogs', $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that modified data causes failures.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
*/
|
||||
public function test_modified_data(string $method): void {
|
||||
$this->require_sodium($method);
|
||||
|
||||
$encrypted = encryption::encrypt('frogs', $method);
|
||||
$mainbit = base64_decode(substr($encrypted, strlen($method) + 1));
|
||||
$mainbit = substr($mainbit, 0, 16) . 'X' . substr($mainbit, 16);
|
||||
$encrypted = $method . ':' . base64_encode($mainbit);
|
||||
$this->expectExceptionMessage('Integrity check failed');
|
||||
encryption::decrypt($encrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests encryption and decryption for real.
|
||||
*
|
||||
* @dataProvider encryption_method_provider
|
||||
* @param string $method Encryption method
|
||||
* @throws \moodle_exception
|
||||
*/
|
||||
public function test_encrypt_and_decrypt_realdata(string $method): void {
|
||||
$this->require_sodium($method);
|
||||
|
||||
// Encrypt short string.
|
||||
$encrypted = encryption::encrypt('frogs', $method);
|
||||
$this->assertNotEquals('frogs', $encrypted);
|
||||
$this->assertEquals('frogs', encryption::decrypt($encrypted));
|
||||
|
||||
// Encrypt really long string (1 MB).
|
||||
$long = str_repeat('X', 1024 * 1024);
|
||||
$this->assertEquals($long, encryption::decrypt(encryption::encrypt($long, $method)));
|
||||
}
|
||||
}
|
31
lib/tests/fixtures/testable_encryption.php
vendored
Normal file
31
lib/tests/fixtures/testable_encryption.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?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/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Testable version of the encryption class - just makes it possible to unit-test protected
|
||||
* function.
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2020 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class testable_encryption extends encryption {
|
||||
public static function get_key(?string $method = null): string {
|
||||
return parent::get_key($method);
|
||||
}
|
||||
}
|
@ -109,7 +109,7 @@
|
||||
<location>simplepie</location>
|
||||
<name>SimplePie</name>
|
||||
<license>BSD</license>
|
||||
<version>1.5.3</version>
|
||||
<version>1.5.6</version>
|
||||
<licenseversion></licenseversion>
|
||||
</library>
|
||||
<library>
|
||||
@ -158,7 +158,7 @@
|
||||
<location>html2text</location>
|
||||
<name>HTML2Text</name>
|
||||
<license>GPL</license>
|
||||
<version>4.2.1</version>
|
||||
<version>4.3.1</version>
|
||||
<licenseversion>2.0+</licenseversion>
|
||||
</library>
|
||||
<library>
|
||||
|
@ -7,6 +7,13 @@ information provided here is intended especially for developers.
|
||||
* Final deprecation i_dock_block() in behat_deprecated.php
|
||||
* Final deprecation of get_courses_page. Function has been removed and core_course_category::get_courses() should be
|
||||
used instead.
|
||||
* New encryption API in \core\encryption allows secure encryption and decryption of data. By
|
||||
default the key is stored in moodledata but admins can configure a different, more secure
|
||||
location in config.php if required. To get the best possible security for this feature, we
|
||||
recommend enabling the Sodium PHP extension.
|
||||
* Behat timeout constants behat_base::TIMEOUT, EXTENDED_TIMEOUT, and REDUCED_TIMEOUT, which were deprecated in 3.7, have been removed.
|
||||
* \core_table\local\filter\filterset::JOINTYPE_DEFAULT is being changed from 1 (ANY) to 2 (ALL). Filterset implementations
|
||||
can override the default filterset join type by overriding \core_table\local\filter\filterset::get_join_type() instead.
|
||||
|
||||
=== 3.10 ===
|
||||
* PHPUnit has been upgraded to 8.5. That comes with a few changes:
|
||||
|
@ -76,6 +76,7 @@ M.mod_assign.init_grading_table = function(Y) {
|
||||
var batchform = Y.one('form.gradingbatchoperationsform');
|
||||
if (batchform) {
|
||||
batchform.on('submit', function(e) {
|
||||
M.util.js_pending('mod_assign/module.js:batch:submit');
|
||||
checkboxes = Y.all('td.c0 input');
|
||||
var selectedusers = [];
|
||||
checkboxes.each(function(node) {
|
||||
@ -102,8 +103,10 @@ M.mod_assign.init_grading_table = function(Y) {
|
||||
confirmmessage = M.util.get_string('batchoperationconfirm' + operation.get('value'), 'assign');
|
||||
}
|
||||
if (!confirm(confirmmessage)) {
|
||||
M.util.js_complete('mod_assign/module.js:batch:submit');
|
||||
e.preventDefault();
|
||||
}
|
||||
// Note: Do not js_complete. The page being reloaded will empty it.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -823,8 +823,8 @@ class manager {
|
||||
}
|
||||
}
|
||||
|
||||
// Add all supported block contexts, in a single query for performance.
|
||||
if (!empty($areasbylevel[CONTEXT_BLOCK])) {
|
||||
// Add all supported block contexts for course contexts that user can access, in a single query for performance.
|
||||
if (!empty($areasbylevel[CONTEXT_BLOCK]) && !empty($coursecontextids)) {
|
||||
// Get list of all block types we care about.
|
||||
$blocklist = [];
|
||||
foreach ($areasbylevel[CONTEXT_BLOCK] as $areaid => $searchclass) {
|
||||
|
@ -791,7 +791,6 @@ class search_manager_testcase extends advanced_testcase {
|
||||
$this->assertEquals($contexts['block_html-content'], $limitedcontexts['block_html-content']);
|
||||
|
||||
// Get block context ids for the blocks that appear.
|
||||
global $DB;
|
||||
$blockcontextids = $DB->get_fieldset_sql('
|
||||
SELECT x.id
|
||||
FROM {block_instances} bi
|
||||
@ -811,6 +810,43 @@ class search_manager_testcase extends advanced_testcase {
|
||||
$this->assertCount(1, $contexts['block_html-content']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests retrieval of users search areas when limiting to a course the user is not enrolled in
|
||||
*/
|
||||
public function test_search_users_accesses_limit_non_enrolled_course() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->setUser($user);
|
||||
|
||||
$search = testable_core_search::instance();
|
||||
$search->add_core_search_areas();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$context = context_course::instance($course->id);
|
||||
|
||||
// Limit courses to search to only those the user is enrolled in.
|
||||
set_config('searchallavailablecourses', 0);
|
||||
|
||||
$usercontexts = $search->get_areas_user_accesses([$course->id])->usercontexts;
|
||||
$this->assertNotEmpty($usercontexts);
|
||||
$this->assertArrayNotHasKey('core_course-course', $usercontexts);
|
||||
|
||||
// This config ensures the search will also include courses the user can view.
|
||||
set_config('searchallavailablecourses', 1);
|
||||
|
||||
// Allow "Authenticated user" role to view the course without being enrolled in it.
|
||||
$userrole = $DB->get_record('role', ['shortname' => 'user'], '*', MUST_EXIST);
|
||||
role_change_permission($userrole->id, $context, 'moodle/course:view', CAP_ALLOW);
|
||||
|
||||
$usercontexts = $search->get_areas_user_accesses([$course->id])->usercontexts;
|
||||
$this->assertNotEmpty($usercontexts);
|
||||
$this->assertArrayHasKey('core_course-course', $usercontexts);
|
||||
$this->assertEquals($context->id, reset($usercontexts['core_course-course']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_areas_user_accesses with regard to the 'all available courses' config option.
|
||||
*
|
||||
|
2
theme/boost/amd/build/aria.min.js
vendored
2
theme/boost/amd/build/aria.min.js
vendored
@ -1,2 +1,2 @@
|
||||
define ("theme_boost/aria",["jquery","core/pending"],function(a,b){return{init:function init(){var c=!1,d=function(){c=!0},e=function(){var a=c;c=!1;return a};a("[data-toggle=\"dropdown\"]").keydown(function(b){var c=b.which||b.keyCode,e;if(38==c){d()}if(27==c){e=a(b.target).attr("aria-expanded");b.preventDefault();if("false"==e){a(b.target).click()}}if(32==c||13==c){b.preventDefault();a(b.target).click()}});var f=function(c){var d=function(b){a(this).focus();b.resolve()}.bind(c);setTimeout(d,50,new b("core/aria:delayed-focus"))};a(".dropdown").on("shown.bs.dropdown",function(b){var c=a(b.target).find("[role=\"menu\"]"),d=!1,g=!1;if(c){d=a(c).find("[role=\"menuitem\"]")}if(d&&0<d.length){if(e()){g=d[d.length-1]}else{g=d[0]}}if(g){f(g)}});a(".dropdown [role=\"menu\"] [role=\"menuitem\"]").keypress(function(b){var c=String.fromCharCode(b.which||b.keyCode),d=a(b.target).closest("[role=\"menu\"]"),e=0,g=!1,h,j;if(!d){return}g=a(d).find("[role=\"menuitem\"]");if(!g){return}c=c.toLowerCase();for(e=0;e<g.length;e++){h=a(g[e]);j=h.text().trim().toLowerCase();if(0==j.indexOf(c)){f(h);break}}});a(".dropdown [role=\"menu\"] [role=\"menuitem\"]").keydown(function(b){var c=b.which||b.keyCode,d=!1,e=a(b.target).closest("[role=\"menu\"]"),g=0,h=!1;if(!e){return}h=a(e).find("[role=\"menuitem\"]");if(!h){return}if(40==c){for(g=0;g<h.length-1;g++){if(h[g]==b.target){d=h[g+1];break}}if(!d){d=h[0]}}else if(38==c){for(g=1;g<h.length;g++){if(h[g]==b.target){d=h[g-1];break}}if(!d){d=h[h.length-1]}}else if(36==c){d=h[0]}else if(35==c){d=h[h.length-1]}if(d){b.preventDefault();f(d)}});a(".dropdown").on("hidden.bs.dropdown",function(b){var c=a(b.target).find("[data-toggle=\"dropdown\"]");if(c){f(c)}});window.addEventListener("load",function(){var a=document.querySelectorAll("[data-aria-autofocus=\"true\"][role=\"alert\"]");Array.prototype.forEach.call(a,function(a){a.innerHTML+=" ";a.removeAttribute("data-aria-autofocus")})})}}});
|
||||
define ("theme_boost/aria",["exports","jquery","core/pending"],function(a,b,c){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=d(b);c=d(c);function d(a){return a&&a.__esModule?a:{default:a}}var e=function(){var a=!1,d=function(){a=!0},e=function(){var b=a;a=!1;return b};document.addEventListener("keydown",function(a){if(a.target.matches("[data-toggle=\"dropdown\"]")){var b=a.key;if("ArrowUp"==b){d()}if(" "==b||"Enter"==b){a.preventDefault();a.target.click()}}});var f=function(a){setTimeout(function delayedFocus(b){a.focus();b.resolve()},50,new c.default("core/aria:delayed-focus"))};(0,b.default)(".dropdown").on("shown.bs.dropdown",function(a){var b=a.target.querySelector("[role=\"menu\"]"),c=!1,d=!1;if(b){c=b.querySelectorAll("[role=\"menuitem\"]")}if(c&&0<c.length){if(e()){d=c[c.length-1]}else{d=c[0]}}if(d){f(d)}});document.addEventListener("keypress",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var g=a.target.closest("[role=\"menu\"]");if(!g){return}var h=g.querySelectorAll("[role=\"menuitem\"]");if(!h){return}for(var b=a.key.toLowerCase(),c=0;c<h.length;c++){var d=h[c],e=d.text.trim().toLowerCase();if(0==e.indexOf(b)){f(d);break}}}});document.addEventListener("keydown",function(a){if(a.target.matches(".dropdown [role=\"menu\"] [role=\"menuitem\"]")){var b=a.key,c=!1,d=a.target.closest("[role=\"menu\"]");if(!d){return}var e=d.querySelectorAll("[role=\"menuitem\"]");if(!e){return}if("ArrowDown"==b){for(var g=0;g<e.length-1;g++){if(e[g]==a.target){c=e[g+1];break}}if(!c){c=e[0]}}else if("ArrowUp"==b){for(var h=1;h<e.length;h++){if(e[h]==a.target){c=e[h-1];break}}if(!c){c=e[e.length-1]}}else if("Home"==b){c=e[0]}else if("End"==b){c=e[e.length-1]}if(c){a.preventDefault();f(c)}}});(0,b.default)(".dropdown").on("hidden.bs.dropdown",function(a){var b=a.target.querySelector("[data-toggle=\"dropdown\"]");if(b){f(b)}})},f=function(){window.addEventListener("load",function(){var a=document.querySelectorAll("[data-aria-autofocus=\"true\"][role=\"alert\"]");Array.prototype.forEach.call(a,function(a){a.innerHTML+=" ";a.removeAttribute("data-aria-autofocus")})})},g=function(a){for(var c=a.target.closest("[role=\"tablist\"]"),d="vertical"==c.getAttribute("aria-orientation"),e=window.right_to_left(),f=d?"ArrowDown":e?"ArrowLeft":"ArrowRight",g=d?"ArrowUp":e?"ArrowRight":"ArrowLeft",h=Array.prototype.filter.call(c.querySelectorAll("[role=\"tab\"]"),function(a){return"none"!==getComputedStyle(a).display}),j=0;j<h.length;j++){h[j].index=j}switch(a.key){case f:a.preventDefault();if(a.target.index!==void 0&&h[a.target.index+1]){h[a.target.index+1].focus()}else{h[0].focus()}break;case g:a.preventDefault();if(a.target.index!==void 0&&h[a.target.index-1]){h[a.target.index-1].focus()}else{h[h.length-1].focus()}break;case"Home":a.preventDefault();h[0].focus();break;case"End":a.preventDefault();h[h.length-1].focus();break;case"Enter":case" ":a.preventDefault();(0,b.default)(a.target).tab("show");h.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0;}},h=function(){document.addEventListener("keydown",function(a){if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(a.key)){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){g(a)}}});document.addEventListener("click",function(a){if(a.target.matches("[role=\"tablist\"] [role=\"tab\"]")){var c=a.target.closest("[role=\"tablist\"]").querySelectorAll("[role=\"tab\"]");a.preventDefault();(0,b.default)(a.target).tab("show");c.forEach(function(a){a.tabIndex=-1});a.target.tabIndex=0}})};a.init=function init(){e();f();h()}});
|
||||
//# sourceMappingURL=aria.min.js.map
|
||||
|
File diff suppressed because one or more lines are too long
2
theme/boost/amd/build/loader.min.js
vendored
2
theme/boost/amd/build/loader.min.js
vendored
@ -1,2 +1,2 @@
|
||||
define ("theme_boost/loader",["exports","jquery","./aria","./bootstrap/index","core/pending","./scroll","./pending"],function(a,b,c,d,e,f,g){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Bootstrap",{enumerable:!0,get:function get(){return d.default}});b=h(b);c=h(c);d=h(d);e=h(e);f=h(f);g=h(g);function h(a){return a&&a.__esModule?a:{default:a}}var i=function(){(0,b.default)("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(a){var c=(0,b.default)(a.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var a=window.location.hash;if(a){(0,b.default)(".nav-link[href=\""+a+"\"]").tab("show")}},j=function(){(0,b.default)("body").popover({container:"body",selector:"[data-toggle=\"popover\"]",trigger:"focus"});document.addEventListener("keydown",function(a){if("Escape"===a.key&&a.target.closest("[data-toggle=\"popover\"]")){(0,b.default)(a.target).popover("hide")}})},k=function(){(0,b.default)("body").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"})},l=new e.default("theme_boost/loader:init");(0,g.default)();i();j();k();new f.default().init();b.default.fn.dropdown.Constructor.Default.flip=!1;c.default.init();l.resolve()});
|
||||
function _typeof(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){_typeof=function(a){return typeof a}}else{_typeof=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return _typeof(a)}define ("theme_boost/loader",["exports","jquery","./aria","./bootstrap/index","core/pending","./scroll","./pending"],function(a,b,c,d,e,f,g){"use strict";Object.defineProperty(a,"__esModule",{value:!0});Object.defineProperty(a,"Bootstrap",{enumerable:!0,get:function get(){return d.default}});b=j(b);c=i(c);d=j(d);e=j(e);f=j(f);g=j(g);function h(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;h=function(){return a};return a}function i(a){if(a&&a.__esModule){return a}if(null===a||"object"!==_typeof(a)&&"function"!=typeof a){return{default:a}}var b=h();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var e in a){if(Object.prototype.hasOwnProperty.call(a,e)){var f=d?Object.getOwnPropertyDescriptor(a,e):null;if(f&&(f.get||f.set)){Object.defineProperty(c,e,f)}else{c[e]=a[e]}}}c.default=a;if(b){b.set(a,c)}return c}function j(a){return a&&a.__esModule?a:{default:a}}var k=function(){(0,b.default)("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(a){var c=(0,b.default)(a.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var a=window.location.hash;if(a){var c=document.querySelector(".nav-link[href=\""+a+"\"]");if(c){c.click()}}},l=function(){(0,b.default)("body").popover({container:"body",selector:"[data-toggle=\"popover\"]",trigger:"focus"});document.addEventListener("keydown",function(a){if("Escape"===a.key&&a.target.closest("[data-toggle=\"popover\"]")){(0,b.default)(a.target).popover("hide")}})},m=function(){(0,b.default)("body").tooltip({container:"body",selector:"[data-toggle=\"tooltip\"]"})},n=new e.default("theme_boost/loader:init");(0,g.default)();c.init();k();l();m();new f.default().init();b.default.fn.dropdown.Constructor.Default.flip=!1;n.resolve()});
|
||||
//# sourceMappingURL=loader.min.js.map
|
||||
|
@ -1 +1 @@
|
||||
{"version":3,"sources":["../src/loader.js"],"names":["rememberTabs","on","e","hash","target","attr","history","replaceState","location","window","tab","enablePopovers","popover","container","selector","trigger","document","addEventListener","key","closest","enableTooltips","tooltip","pendingPromise","Pending","Scroll","init","$","fn","dropdown","Constructor","Default","flip","Aria","resolve"],"mappings":"qSA0BA,OACA,OACA,OACA,OACA,OACA,O,sDAKMA,CAAAA,CAAY,CAAG,UAAM,CACvB,cAAE,wBAAF,EAA0BC,EAA1B,CAA6B,cAA7B,CAA6C,SAASC,CAAT,CAAY,CACrD,GAAIC,CAAAA,CAAI,CAAG,cAAED,CAAC,CAACE,MAAJ,EAAYC,IAAZ,CAAiB,MAAjB,CAAX,CACA,GAAIC,OAAO,CAACC,YAAZ,CAA0B,CACtBD,OAAO,CAACC,YAAR,CAAqB,IAArB,CAA2B,IAA3B,CAAiCJ,CAAjC,CACH,CAFD,IAEO,CACHK,QAAQ,CAACL,IAAT,CAAgBA,CACnB,CACJ,CAPD,EAQA,GAAIA,CAAAA,CAAI,CAAGM,MAAM,CAACD,QAAP,CAAgBL,IAA3B,CACA,GAAIA,CAAJ,CAAU,CACP,cAAE,oBAAqBA,CAArB,CAA4B,KAA9B,EAAoCO,GAApC,CAAwC,MAAxC,CACF,CACJ,C,CAMKC,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdC,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAGdC,OAAO,CAAE,OAHK,CAAlB,EAMAC,QAAQ,CAACC,gBAAT,CAA0B,SAA1B,CAAqC,SAAAf,CAAC,CAAI,CACtC,GAAc,QAAV,GAAAA,CAAC,CAACgB,GAAF,EAAsBhB,CAAC,CAACE,MAAF,CAASe,OAAT,CAAiB,2BAAjB,CAA1B,CAAuE,CACnE,cAAEjB,CAAC,CAACE,MAAJ,EAAYQ,OAAZ,CAAoB,MAApB,CACH,CACJ,CAJD,CAKH,C,CAMKQ,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdR,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAAlB,CAIH,C,CAEKQ,CAAc,CAAG,GAAIC,UAAJ,CAAY,yBAAZ,C,CAGvB,gBAGAvB,CAAY,GAGZW,CAAc,GAGdS,CAAc,GAGb,GAAII,UAAJ,EAAD,CAAeC,IAAf,GAGAC,UAAEC,EAAF,CAAKC,QAAL,CAAcC,WAAd,CAA0BC,OAA1B,CAAkCC,IAAlC,IAGAC,UAAKP,IAAL,GAEAH,CAAc,CAACW,OAAf,E","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese <damyon@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport Aria from './aria';\nimport Bootstrap from './bootstrap/index';\nimport Pending from 'core/pending';\nimport Scroll from './scroll';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = $(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n var hash = window.location.hash;\n if (hash) {\n $('.nav-link[href=\"' + hash + '\"]').tab('show');\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n $('body').popover({\n container: 'body',\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus',\n });\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('hide');\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n $('body').tooltip({\n container: 'body',\n selector: '[data-toggle=\"tooltip\"]',\n });\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Add scroll handling.\n(new Scroll()).init();\n\n// Disables flipping the dropdowns up and getting hidden behind the navbar.\n$.fn.dropdown.Constructor.Default.flip = false;\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"file":"loader.min.js"}
|
||||
{"version":3,"sources":["../src/loader.js"],"names":["rememberTabs","on","e","hash","target","attr","history","replaceState","location","window","tab","document","querySelector","click","enablePopovers","popover","container","selector","trigger","addEventListener","key","closest","enableTooltips","tooltip","pendingPromise","Pending","Aria","init","Scroll","$","fn","dropdown","Constructor","Default","flip","resolve"],"mappings":"wkBA0BA,OACA,OACA,OACA,OACA,OACA,O,4lBAKMA,CAAAA,CAAY,CAAG,UAAM,CACvB,cAAE,wBAAF,EAA0BC,EAA1B,CAA6B,cAA7B,CAA6C,SAASC,CAAT,CAAY,CACrD,GAAIC,CAAAA,CAAI,CAAG,cAAED,CAAC,CAACE,MAAJ,EAAYC,IAAZ,CAAiB,MAAjB,CAAX,CACA,GAAIC,OAAO,CAACC,YAAZ,CAA0B,CACtBD,OAAO,CAACC,YAAR,CAAqB,IAArB,CAA2B,IAA3B,CAAiCJ,CAAjC,CACH,CAFD,IAEO,CACHK,QAAQ,CAACL,IAAT,CAAgBA,CACnB,CACJ,CAPD,EAQA,GAAMA,CAAAA,CAAI,CAAGM,MAAM,CAACD,QAAP,CAAgBL,IAA7B,CACA,GAAIA,CAAJ,CAAU,CACN,GAAMO,CAAAA,CAAG,CAAGC,QAAQ,CAACC,aAAT,CAAuB,oBAAqBT,CAArB,CAA4B,KAAnD,CAAZ,CACA,GAAIO,CAAJ,CAAS,CACLA,CAAG,CAACG,KAAJ,EACH,CACJ,CACJ,C,CAMKC,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdC,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAGdC,OAAO,CAAE,OAHK,CAAlB,EAMAP,QAAQ,CAACQ,gBAAT,CAA0B,SAA1B,CAAqC,SAAAjB,CAAC,CAAI,CACtC,GAAc,QAAV,GAAAA,CAAC,CAACkB,GAAF,EAAsBlB,CAAC,CAACE,MAAF,CAASiB,OAAT,CAAiB,2BAAjB,CAA1B,CAAuE,CACnE,cAAEnB,CAAC,CAACE,MAAJ,EAAYW,OAAZ,CAAoB,MAApB,CACH,CACJ,CAJD,CAKH,C,CAMKO,CAAc,CAAG,UAAM,CACzB,cAAE,MAAF,EAAUC,OAAV,CAAkB,CACdP,SAAS,CAAE,MADG,CAEdC,QAAQ,CAAE,2BAFI,CAAlB,CAIH,C,CAEKO,CAAc,CAAG,GAAIC,UAAJ,CAAY,yBAAZ,C,CAGvB,gBAGAC,CAAI,CAACC,IAAL,GAGA3B,CAAY,GAGZc,CAAc,GAGdQ,CAAc,GAGb,GAAIM,UAAJ,EAAD,CAAeD,IAAf,GAGAE,UAAEC,EAAF,CAAKC,QAAL,CAAcC,WAAd,CAA0BC,OAA1B,CAAkCC,IAAlC,IAEAV,CAAc,CAACW,OAAf,E","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese <damyon@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\n\nimport $ from 'jquery';\nimport * as Aria from './aria';\nimport Bootstrap from './bootstrap/index';\nimport Pending from 'core/pending';\nimport Scroll from './scroll';\nimport setupBootstrapPendingChecks from './pending';\n\n/**\n * Rember the last visited tabs.\n */\nconst rememberTabs = () => {\n $('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = $(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n const hash = window.location.hash;\n if (hash) {\n const tab = document.querySelector('.nav-link[href=\"' + hash + '\"]');\n if (tab) {\n tab.click();\n }\n }\n};\n\n/**\n * Enable all popovers\n *\n */\nconst enablePopovers = () => {\n $('body').popover({\n container: 'body',\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus',\n });\n\n document.addEventListener('keydown', e => {\n if (e.key === 'Escape' && e.target.closest('[data-toggle=\"popover\"]')) {\n $(e.target).popover('hide');\n }\n });\n};\n\n/**\n * Enable tooltips\n *\n */\nconst enableTooltips = () => {\n $('body').tooltip({\n container: 'body',\n selector: '[data-toggle=\"tooltip\"]',\n });\n};\n\nconst pendingPromise = new Pending('theme_boost/loader:init');\n\n// Add pending promise event listeners to relevant Bootstrap custom events.\nsetupBootstrapPendingChecks();\n\n// Setup Aria helpers for Bootstrap features.\nAria.init();\n\n// Remember the last visited tabs.\nrememberTabs();\n\n// Enable all popovers.\nenablePopovers();\n\n// Enable all tooltips.\nenableTooltips();\n\n// Add scroll handling.\n(new Scroll()).init();\n\n// Disables flipping the dropdowns up and getting hidden behind the navbar.\n$.fn.dropdown.Constructor.Default.flip = false;\n\npendingPromise.resolve();\n\nexport {\n Bootstrap,\n};\n"],"file":"loader.min.js"}
|
@ -20,181 +20,263 @@
|
||||
* @copyright 2018 Damyon Wiese <damyon@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define(['jquery', 'core/pending'], function($, Pending) {
|
||||
return {
|
||||
init: function() {
|
||||
// Drop downs from bootstrap don't support keyboard accessibility by default.
|
||||
var focusEnd = false,
|
||||
setFocusEnd = function() {
|
||||
focusEnd = true;
|
||||
},
|
||||
getFocusEnd = function() {
|
||||
var result = focusEnd;
|
||||
focusEnd = false;
|
||||
return result;
|
||||
};
|
||||
|
||||
// Special handling for "up" keyboard control.
|
||||
$('[data-toggle="dropdown"]').keydown(function(e) {
|
||||
var trigger = e.which || e.keyCode,
|
||||
expanded;
|
||||
import $ from 'jquery';
|
||||
import Pending from 'core/pending';
|
||||
|
||||
// Up key opens the menu at the end.
|
||||
if (trigger == 38) {
|
||||
// Focus the end of the menu, not the beginning.
|
||||
setFocusEnd();
|
||||
/**
|
||||
* Drop downs from bootstrap don't support keyboard accessibility by default.
|
||||
*/
|
||||
const dropdownFix = () => {
|
||||
let focusEnd = false;
|
||||
const setFocusEnd = () => {
|
||||
focusEnd = true;
|
||||
};
|
||||
const getFocusEnd = () => {
|
||||
const result = focusEnd;
|
||||
focusEnd = false;
|
||||
return result;
|
||||
};
|
||||
|
||||
// Special handling for "up" keyboard control.
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.target.matches('[data-toggle="dropdown"]')) {
|
||||
const trigger = e.key;
|
||||
|
||||
// Up key opens the menu at the end.
|
||||
if (trigger == 'ArrowUp') {
|
||||
// Focus the end of the menu, not the beginning.
|
||||
setFocusEnd();
|
||||
}
|
||||
|
||||
// Space key or Enter key opens the menu.
|
||||
if (trigger == ' ' || trigger == 'Enter') {
|
||||
// Cancel random scroll.
|
||||
e.preventDefault();
|
||||
// Open the menu instead.
|
||||
e.target.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Special handling for navigation keys when menu is open.
|
||||
const shiftFocus = element => {
|
||||
const delayedFocus = pendingPromise => {
|
||||
element.focus();
|
||||
pendingPromise.resolve();
|
||||
};
|
||||
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
|
||||
};
|
||||
|
||||
$('.dropdown').on('shown.bs.dropdown', e => {
|
||||
// We need to focus on the first menuitem.
|
||||
const menu = e.target.querySelector('[role="menu"]');
|
||||
let menuItems = false;
|
||||
let foundMenuItem = false;
|
||||
|
||||
if (menu) {
|
||||
menuItems = menu.querySelectorAll('[role="menuitem"]');
|
||||
}
|
||||
if (menuItems && menuItems.length > 0) {
|
||||
if (getFocusEnd()) {
|
||||
foundMenuItem = menuItems[menuItems.length - 1];
|
||||
} else {
|
||||
// The first menu entry, pretty reasonable.
|
||||
foundMenuItem = menuItems[0];
|
||||
}
|
||||
}
|
||||
if (foundMenuItem) {
|
||||
shiftFocus(foundMenuItem);
|
||||
}
|
||||
});
|
||||
// Search for menu items by finding the first item that has
|
||||
// text starting with the typed character (case insensitive).
|
||||
document.addEventListener('keypress', e => {
|
||||
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
|
||||
const menu = e.target.closest('[role="menu"]');
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
const menuItems = menu.querySelectorAll('[role="menuitem"]');
|
||||
if (!menuItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
const trigger = e.key.toLowerCase();
|
||||
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
const item = menuItems[i];
|
||||
const itemText = item.text.trim().toLowerCase();
|
||||
if (itemText.indexOf(trigger) == 0) {
|
||||
shiftFocus(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Escape key only closes the menu, it doesn't open it.
|
||||
if (trigger == 27) {
|
||||
expanded = $(e.target).attr('aria-expanded');
|
||||
e.preventDefault();
|
||||
if (expanded == "false") {
|
||||
$(e.target).click();
|
||||
}
|
||||
}
|
||||
// Keyboard navigation for arrow keys, home and end keys.
|
||||
document.addEventListener('keydown', e => {
|
||||
if (e.target.matches('.dropdown [role="menu"] [role="menuitem"]')) {
|
||||
const trigger = e.key;
|
||||
let next = false;
|
||||
const menu = e.target.closest('[role="menu"]');
|
||||
|
||||
// Space key or Enter key opens the menu.
|
||||
if (trigger == 32 || trigger == 13) {
|
||||
// Cancel random scroll.
|
||||
e.preventDefault();
|
||||
// Open the menu instead.
|
||||
$(e.target).click();
|
||||
}
|
||||
});
|
||||
|
||||
// Special handling for navigation keys when menu is open.
|
||||
var shiftFocus = function(element) {
|
||||
var delayedFocus = function(pendingPromise) {
|
||||
$(this).focus();
|
||||
pendingPromise.resolve();
|
||||
}.bind(element);
|
||||
setTimeout(delayedFocus, 50, new Pending('core/aria:delayed-focus'));
|
||||
};
|
||||
|
||||
$('.dropdown').on('shown.bs.dropdown', function(e) {
|
||||
// We need to focus on the first menuitem.
|
||||
var menu = $(e.target).find('[role="menu"]'),
|
||||
menuItems = false,
|
||||
foundMenuItem = false;
|
||||
|
||||
if (menu) {
|
||||
menuItems = $(menu).find('[role="menuitem"]');
|
||||
}
|
||||
if (menuItems && menuItems.length > 0) {
|
||||
if (getFocusEnd()) {
|
||||
foundMenuItem = menuItems[menuItems.length - 1];
|
||||
} else {
|
||||
// The first menu entry, pretty reasonable.
|
||||
foundMenuItem = menuItems[0];
|
||||
}
|
||||
}
|
||||
if (foundMenuItem) {
|
||||
shiftFocus(foundMenuItem);
|
||||
}
|
||||
});
|
||||
// Search for menu items by finding the first item that has
|
||||
// text starting with the typed character (case insensitive).
|
||||
$('.dropdown [role="menu"] [role="menuitem"]').keypress(function(e) {
|
||||
var trigger = String.fromCharCode(e.which || e.keyCode),
|
||||
menu = $(e.target).closest('[role="menu"]'),
|
||||
i = 0,
|
||||
menuItems = false,
|
||||
item,
|
||||
itemText;
|
||||
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
menuItems = $(menu).find('[role="menuitem"]');
|
||||
if (!menuItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
trigger = trigger.toLowerCase();
|
||||
for (i = 0; i < menuItems.length; i++) {
|
||||
item = $(menuItems[i]);
|
||||
itemText = item.text().trim().toLowerCase();
|
||||
if (itemText.indexOf(trigger) == 0) {
|
||||
shiftFocus(item);
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
const menuItems = menu.querySelectorAll('[role="menuitem"]');
|
||||
if (!menuItems) {
|
||||
return;
|
||||
}
|
||||
// Down key.
|
||||
if (trigger == 'ArrowDown') {
|
||||
for (let i = 0; i < menuItems.length - 1; i++) {
|
||||
if (menuItems[i] == e.target) {
|
||||
next = menuItems[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard navigation for arrow keys, home and end keys.
|
||||
$('.dropdown [role="menu"] [role="menuitem"]').keydown(function(e) {
|
||||
var trigger = e.which || e.keyCode,
|
||||
next = false,
|
||||
menu = $(e.target).closest('[role="menu"]'),
|
||||
i = 0,
|
||||
menuItems = false;
|
||||
if (!menu) {
|
||||
return;
|
||||
}
|
||||
menuItems = $(menu).find('[role="menuitem"]');
|
||||
if (!menuItems) {
|
||||
return;
|
||||
}
|
||||
// Down key.
|
||||
if (trigger == 40) {
|
||||
for (i = 0; i < menuItems.length - 1; i++) {
|
||||
if (menuItems[i] == e.target) {
|
||||
next = menuItems[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!next) {
|
||||
// Wrap to first item.
|
||||
next = menuItems[0];
|
||||
}
|
||||
|
||||
} else if (trigger == 38) {
|
||||
// Up key.
|
||||
for (i = 1; i < menuItems.length; i++) {
|
||||
if (menuItems[i] == e.target) {
|
||||
next = menuItems[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!next) {
|
||||
// Wrap to last item.
|
||||
next = menuItems[menuItems.length - 1];
|
||||
}
|
||||
|
||||
} else if (trigger == 36) {
|
||||
// Home key.
|
||||
if (!next) {
|
||||
// Wrap to first item.
|
||||
next = menuItems[0];
|
||||
}
|
||||
|
||||
} else if (trigger == 35) {
|
||||
// End key.
|
||||
} else if (trigger == 'ArrowUp') {
|
||||
// Up key.
|
||||
for (let i = 1; i < menuItems.length; i++) {
|
||||
if (menuItems[i] == e.target) {
|
||||
next = menuItems[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!next) {
|
||||
// Wrap to last item.
|
||||
next = menuItems[menuItems.length - 1];
|
||||
}
|
||||
// Variable next is set if we do want to act on the keypress.
|
||||
if (next) {
|
||||
e.preventDefault();
|
||||
shiftFocus(next);
|
||||
}
|
||||
return;
|
||||
});
|
||||
$('.dropdown').on('hidden.bs.dropdown', function(e) {
|
||||
// We need to focus on the menu trigger.
|
||||
var trigger = $(e.target).find('[data-toggle="dropdown"]');
|
||||
if (trigger) {
|
||||
shiftFocus(trigger);
|
||||
}
|
||||
});
|
||||
|
||||
// After page load, focus on any element with special autofocus attribute.
|
||||
window.addEventListener("load", () => {
|
||||
const alerts = document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');
|
||||
Array.prototype.forEach.call(alerts, autofocusElement => {
|
||||
// According to the specification an role="alert" region is only read out on change to the content
|
||||
// of that region.
|
||||
autofocusElement.innerHTML += ' ';
|
||||
autofocusElement.removeAttribute('data-aria-autofocus');
|
||||
});
|
||||
});
|
||||
} else if (trigger == 'Home') {
|
||||
// Home key.
|
||||
next = menuItems[0];
|
||||
|
||||
} else if (trigger == 'End') {
|
||||
// End key.
|
||||
next = menuItems[menuItems.length - 1];
|
||||
}
|
||||
// Variable next is set if we do want to act on the keypress.
|
||||
if (next) {
|
||||
e.preventDefault();
|
||||
shiftFocus(next);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
$('.dropdown').on('hidden.bs.dropdown', e => {
|
||||
// We need to focus on the menu trigger.
|
||||
const trigger = e.target.querySelector('[data-toggle="dropdown"]');
|
||||
if (trigger) {
|
||||
shiftFocus(trigger);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* After page load, focus on any element with special autofocus attribute.
|
||||
*/
|
||||
const autoFocus = () => {
|
||||
window.addEventListener("load", () => {
|
||||
const alerts = document.querySelectorAll('[data-aria-autofocus="true"][role="alert"]');
|
||||
Array.prototype.forEach.call(alerts, autofocusElement => {
|
||||
// According to the specification an role="alert" region is only read out on change to the content
|
||||
// of that region.
|
||||
autofocusElement.innerHTML += ' ';
|
||||
autofocusElement.removeAttribute('data-aria-autofocus');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the focus to the correct tab based on the key that is pressed.
|
||||
* @param {KeyboardEvent} e
|
||||
*/
|
||||
const updateTabFocus = e => {
|
||||
const tabList = e.target.closest('[role="tablist"]');
|
||||
const vertical = tabList.getAttribute('aria-orientation') == 'vertical';
|
||||
const rtl = window.right_to_left();
|
||||
const arrowNext = vertical ? 'ArrowDown' : (rtl ? 'ArrowLeft' : 'ArrowRight');
|
||||
const arrowPrevious = vertical ? 'ArrowUp' : (rtl ? 'ArrowRight' : 'ArrowLeft');
|
||||
const tabs = Array.prototype.filter.call(
|
||||
tabList.querySelectorAll('[role="tab"]'),
|
||||
tab => getComputedStyle(tab).display !== 'none'); // We only work with the visible tabs.
|
||||
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
tabs[i].index = i;
|
||||
}
|
||||
|
||||
switch (e.key) {
|
||||
case arrowNext:
|
||||
e.preventDefault();
|
||||
if (e.target.index !== undefined && tabs[e.target.index + 1]) {
|
||||
tabs[e.target.index + 1].focus();
|
||||
} else {
|
||||
tabs[0].focus();
|
||||
}
|
||||
break;
|
||||
case arrowPrevious:
|
||||
e.preventDefault();
|
||||
if (e.target.index !== undefined && tabs[e.target.index - 1]) {
|
||||
tabs[e.target.index - 1].focus();
|
||||
} else {
|
||||
tabs[tabs.length - 1].focus();
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
e.preventDefault();
|
||||
tabs[0].focus();
|
||||
break;
|
||||
case 'End':
|
||||
e.preventDefault();
|
||||
tabs[tabs.length - 1].focus();
|
||||
break;
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
e.preventDefault();
|
||||
$(e.target).tab('show');
|
||||
tabs.forEach(tab => {
|
||||
tab.tabIndex = -1;
|
||||
});
|
||||
e.target.tabIndex = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fix accessibility issues regarding tab elements focus and their tab order in Bootstrap navs.
|
||||
*/
|
||||
const tabElementFix = () => {
|
||||
document.addEventListener('keydown', e => {
|
||||
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter', ' '].includes(e.key)) {
|
||||
if (e.target.matches('[role="tablist"] [role="tab"]')) {
|
||||
updateTabFocus(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', e => {
|
||||
if (e.target.matches('[role="tablist"] [role="tab"]')) {
|
||||
const tabs = e.target.closest('[role="tablist"]').querySelectorAll('[role="tab"]');
|
||||
e.preventDefault();
|
||||
$(e.target).tab('show');
|
||||
tabs.forEach(tab => {
|
||||
tab.tabIndex = -1;
|
||||
});
|
||||
e.target.tabIndex = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const init = () => {
|
||||
dropdownFix();
|
||||
autoFocus();
|
||||
tabElementFix();
|
||||
};
|
||||
|
@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
import Aria from './aria';
|
||||
import * as Aria from './aria';
|
||||
import Bootstrap from './bootstrap/index';
|
||||
import Pending from 'core/pending';
|
||||
import Scroll from './scroll';
|
||||
@ -43,9 +43,12 @@ const rememberTabs = () => {
|
||||
location.hash = hash;
|
||||
}
|
||||
});
|
||||
var hash = window.location.hash;
|
||||
const hash = window.location.hash;
|
||||
if (hash) {
|
||||
$('.nav-link[href="' + hash + '"]').tab('show');
|
||||
const tab = document.querySelector('.nav-link[href="' + hash + '"]');
|
||||
if (tab) {
|
||||
tab.click();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -83,6 +86,9 @@ const pendingPromise = new Pending('theme_boost/loader:init');
|
||||
// Add pending promise event listeners to relevant Bootstrap custom events.
|
||||
setupBootstrapPendingChecks();
|
||||
|
||||
// Setup Aria helpers for Bootstrap features.
|
||||
Aria.init();
|
||||
|
||||
// Remember the last visited tabs.
|
||||
rememberTabs();
|
||||
|
||||
@ -98,9 +104,6 @@ enableTooltips();
|
||||
// Disables flipping the dropdowns up and getting hidden behind the navbar.
|
||||
$.fn.dropdown.Constructor.Default.flip = false;
|
||||
|
||||
// Setup Aria helpers for Bootstrap features.
|
||||
Aria.init();
|
||||
|
||||
pendingPromise.resolve();
|
||||
|
||||
export {
|
||||
|
@ -39,7 +39,9 @@
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
{{#tabs}}
|
||||
<li class="nav-item">
|
||||
<a href="#{{name}}" class="nav-link {{#active}}active{{/active}}" data-toggle="tab" role="tab">{{displayname}}</a>
|
||||
<a href="#{{name}}" class="nav-link {{#active}}active{{/active}}" data-toggle="tab" role="tab"
|
||||
{{#active}}aria-selected="true"{{/active}}
|
||||
{{^active}}aria-selected="false" tabindex="-1"{{/active}}>{{displayname}}</a>
|
||||
</li>
|
||||
{{/tabs}}
|
||||
</ul>
|
||||
|
2
user/amd/build/participantsfilter.min.js
vendored
2
user/amd/build/participantsfilter.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -337,8 +337,8 @@ export const init = participantsRegionId => {
|
||||
|
||||
if (filters.length === 1) {
|
||||
filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.add('hidden');
|
||||
filterSet.querySelector(Selectors.filterset.fields.join).value = 1;
|
||||
filterSet.dataset.filterverb = 1;
|
||||
filterSet.querySelector(Selectors.filterset.fields.join).value = 2;
|
||||
filterSet.dataset.filterverb = 2;
|
||||
} else {
|
||||
filterSet.querySelector(Selectors.filterset.regions.filtermatch).classList.remove('hidden');
|
||||
}
|
||||
|
@ -40,7 +40,7 @@
|
||||
}
|
||||
}}
|
||||
|
||||
<div id="core_user-participantsfilter-{{uniqid}}" class="filter-group my-2 p-2 bg-light border-radius border" data-table-region="{{tableregionid}}" data-table-course-id="{{courseid}}" data-filterverb="1">
|
||||
<div id="core_user-participantsfilter-{{uniqid}}" class="filter-group my-2 p-2 bg-light border-radius border" data-table-region="{{tableregionid}}" data-table-course-id="{{courseid}}" data-filterverb="2">
|
||||
<div data-filterregion="filtermatch" class="hidden">
|
||||
<label for="core_user-local-participantsfilter-jointype-{{uniqid}}" class="my-0" aria-hidden="true">
|
||||
{{#str}}match, core_user{{/str}}
|
||||
@ -48,8 +48,8 @@
|
||||
<select class="custom-select" data-filterfield="join" id="core_user-local-participantsfilter-jointype-{{uniqid}}"
|
||||
aria-label="{{#str}}filtersetmatchdescription, core_user{{/str}}">
|
||||
<option value="0">{{#str}}none{{/str}}</option>
|
||||
<option selected=selected value="1">{{#str}}any{{/str}}</option>
|
||||
<option value="2">{{#str}}all{{/str}}</option>
|
||||
<option value="1">{{#str}}any{{/str}}</option>
|
||||
<option value="2" selected>{{#str}}all{{/str}}</option>
|
||||
</select>
|
||||
<span aria-hidden="true">{{#str}}matchofthefollowing, core_user{{/str}}</span>
|
||||
</div>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user