first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
<?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/>.
/**
* Privacy class for requesting user data.
*
* @package core_rss
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_rss\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\local\metadata\collection;
use core_privacy\local\request\contextlist;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use core_privacy\local\request\userlist;
use core_privacy\local\request\approved_userlist;
/**
* Privacy class for requesting user data.
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements
\core_privacy\local\metadata\provider,
\core_privacy\local\request\core_userlist_provider,
\core_privacy\local\request\subsystem\provider {
/**
* Return the fields which contain personal data.
*
* @param collection $collection The initialised collection to add items to.
* @return collection A listing of user data stored through this system.
*/
public static function get_metadata(collection $collection): collection {
$collection->add_subsystem_link('core_userkey', [], 'privacy:metadata:core_userkey');
return $collection;
}
/**
* Get the list of contexts that contain user information for the specified user.
*
* @param int $userid The user to search.
* @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
*/
public static function get_contexts_for_userid(int $userid): contextlist {
$sql = "SELECT ctx.id
FROM {user_private_key} k
JOIN {user} u ON k.userid = u.id
JOIN {context} ctx ON ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel
WHERE k.userid = :userid AND k.script = 'rss'";
$params = ['userid' => $userid, 'contextlevel' => CONTEXT_USER];
$contextlist = new contextlist();
$contextlist->add_from_sql($sql, $params);
return $contextlist;
}
/**
* Get the list of users within a specific context.
*
* @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
*/
public static function get_users_in_context(userlist $userlist) {
$context = $userlist->get_context();
if (!$context instanceof \context_user) {
return;
}
\core_userkey\privacy\provider::get_user_contexts_with_script($userlist, $context, 'rss');
}
/**
* Export all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts to export information for.
*/
public static function export_user_data(approved_contextlist $contextlist) {
// If the user has data, then only the CONTEXT_USER should be present so get the first context.
$contexts = $contextlist->get_contexts();
if (count($contexts) == 0) {
return;
}
$context = reset($contexts);
// Sanity check that context is at the user context level, then get the userid.
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
// Export associated userkeys.
\core_userkey\privacy\provider::export_userkeys($context, [], 'rss');
}
/**
* Delete all use data which matches the specified deletion_criteria.
*
* @param context $context A user context.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
// Sanity check that context is at the user context level, then get the userid.
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
$userid = $context->instanceid;
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('rss', $userid);
}
/**
* Delete multiple users within a single context.
*
* @param approved_userlist $userlist The approved context and user information to delete information for.
*/
public static function delete_data_for_users(approved_userlist $userlist) {
$context = $userlist->get_context();
if ($context instanceof \context_user) {
\core_userkey\privacy\provider::delete_userkeys('rss', $context->instanceid);
}
}
/**
* Delete all user data for the specified user, in the specified contexts.
*
* @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
*/
public static function delete_data_for_user(approved_contextlist $contextlist) {
// If the user has data, then only the user context should be present so get the first context.
$contexts = $contextlist->get_contexts();
if (count($contexts) == 0) {
return;
}
$context = reset($contexts);
// Sanity check that context is at the user context level, then get the userid.
if ($context->contextlevel !== CONTEXT_USER) {
return;
}
$userid = $context->instanceid;
// Delete all the userkeys.
\core_userkey\privacy\provider::delete_userkeys('rss', $userid);
}
}
+194
View File
@@ -0,0 +1,194 @@
<?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/>.
/**
* rss/file.php - entry point to serve rss streams
*
* This script simply checks the parameters to construct a $USER
* then finds and calls a function in the relevant component to
* actually check security and create the RSS stream
*
* @package core_rss
* @category rss
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/** NO_DEBUG_DISPLAY - bool, Disable moodle debug and error messages. Set to false to see any errors during RSS generation */
define('NO_DEBUG_DISPLAY', true);
/** NO_MOODLE_COOKIES - bool, Disable the use of sessions/cookies - we recreate $USER for every call. */
define('NO_MOODLE_COOKIES', true);
require_once('../config.php');
require_once($CFG->libdir.'/filelib.php');
require_once($CFG->libdir.'/rsslib.php');
// RSS feeds must be enabled site-wide.
if (empty($CFG->enablerssfeeds)) {
rss_error();
}
// All the arguments are in the path.
$relativepath = get_file_argument();
if (!$relativepath) {
rss_error();
}
// Extract relative path components into variables.
$args = explode('/', trim($relativepath, '/'));
if (count($args) < 5) {
rss_error();
}
$contextid = (int)$args[0];
$token = clean_param($args[1], PARAM_ALPHANUM);
$componentname = clean_param($args[2], PARAM_FILE);
// Check if they have requested a 1.9 RSS feed.
// If token is an int it is a user id (1.9 request).
// If token contains any letters it is a token (2.0 request).
$inttoken = intval($token);
if ($token === "$inttoken") {
// They have requested a feed using a 1.9 url. redirect them to the 2.0 url using the guest account.
$instanceid = clean_param($args[3], PARAM_INT);
// 1.9 URL puts course id where the context id is in 2.0 URLs.
$courseid = $contextid;
unset($contextid);
// Find the context id.
if ($course = $DB->get_record('course', array('id' => $courseid))) {
$modinfo = get_fast_modinfo($course);
foreach ($modinfo->get_instances_of($componentname) as $modinstanceid => $cm) {
if ($modinstanceid == $instanceid) {
$context = context_module::instance($cm->id, IGNORE_MISSING);
break;
}
}
}
if (empty($context)) {
// This shouldnt happen. something bad is going on.
rss_error();
}
// Make sure that $CFG->siteguest is set.
if (empty($CFG->siteguest)) {
if (!$guestid = $DB->get_field('user', 'id', array('username' => 'guest', 'mnethostid' => $CFG->mnet_localhost_id))) {
// Guest does not exist yet, weird.
rss_error();
}
set_config('siteguest', $guestid);
}
$guesttoken = rss_get_token($CFG->siteguest);
// Change forum to mod_forum (for example).
$componentname = 'mod_'.$componentname;
$url = $PAGE->url;
$url->set_slashargument("/{$context->id}/$guesttoken/$componentname/$instanceid/rss.xml");
// Redirect to the 2.0 rss URL.
redirect($url);
} else {
// Authenticate the user from the token.
$userid = rss_get_userid_from_token($token);
if (!$userid) {
rss_error('rsserrorauth', 'rss.xml', 0, '403 Forbidden');
}
}
// Check the context actually exists.
try {
list($context, $course, $cm) = get_context_info_array($contextid);
} catch (dml_missing_record_exception $e) {
rss_error();
}
$PAGE->set_context($context);
$user = get_complete_user_data('id', $userid);
// Let enrol plugins deal with new enrolments if necessary.
enrol_check_plugins($user, false);
\core\session\manager::set_user($user); // For login and capability checks.
try {
$autologinguest = true;
$setwantsurltome = true;
$preventredirect = true;
require_course_login($course, $autologinguest, $cm, $setwantsurltome, $preventredirect);
} catch (Exception $e) {
if (isguestuser()) {
rss_error('rsserrorguest', 'rss.xml', 0, '403 Forbidden');
} else {
rss_error('rsserrorauth', 'rss.xml', 0, '403 Forbidden');
}
}
// Work out which component in Moodle we want (from the frankenstyle name).
$componentdir = core_component::get_component_directory($componentname);
list($type, $plugin) = core_component::normalize_component($componentname);
// Call the component to check/update the feed and tell us the path to the cached file.
$pathname = null;
if (file_exists($componentdir)) {
require_once("$componentdir/rsslib.php");
$functionname = $plugin.'_rss_get_feed';
if (function_exists($functionname)) {
// The $pathname will be null if there was a problem (eg user doesn't have the necessary capabilities).
// NOTE:the component providing the feed must do its own capability checks and security.
try {
$pathname = $functionname($context, $args);
} catch (Exception $e) {
rss_error('rsserror');
}
}
}
// Check that file exists.
if (empty($pathname) || !file_exists($pathname)) {
rss_error();
}
// Send the RSS file to the user!
send_file($pathname, 'rss.xml', 3600); // Cached by browsers for 1 hour.
/**
* Sends an error formatted as an rss file and then exits
*
* @package core_rss
* @category rss
*
* @param string $error the error type, default is rsserror
* @param string $filename the name of the file to created
* @param int $unused
* @param string $statuscode http 1.1 statuscode indicicating the error
* @uses exit
*/
function rss_error($error='rsserror', $filename='rss.xml', $unused=0, $statuscode='404 Not Found') {
header("HTTP/1.1 $statuscode");
header('Content-Disposition: inline; filename="'.$filename.'"');
header('Content-Type: application/xml');
echo rss_geterrorxmlfile($error);
exit;
}
+84
View File
@@ -0,0 +1,84 @@
<?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/>.
/**
* Web service documentation renderer.
* @package core_rss
* @category rss
* @copyright 2010 Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Web service documentation renderer extending the plugin_renderer_base class.
* @package core_rss
* @category rss
* @copyright 2010 Andrew Davis
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_rss_renderer extends plugin_renderer_base {
/**
* Returns the html for the token reset confirmation box
* @return string html
*/
public function user_reset_rss_token_confirmation() {
$managetokenurl = '/user/managetoken.php';
$optionsyes = ['action' => 'resetrsstoken', 'confirm' => 1];
$formcontinue = new single_button(new moodle_url($managetokenurl, $optionsyes), get_string('reset'));
$formcancel = new single_button(new moodle_url($managetokenurl), get_string('cancel'), 'get');
$html = $this->output->confirm(get_string('resettokenconfirmsimple', 'webservice'), $formcontinue, $formcancel);
return $html;
}
/**
* Display a user token with buttons to reset it
* @param string $token The token to be displayed
* @return string html code
*/
public function user_rss_token_box($token) {
global $CFG;
// Display strings.
$stroperation = get_string('operation', 'webservice');
$strtoken = get_string('key', 'webservice');
$return = $this->output->heading(get_string('rss', 'rss'), 3, 'main', true);
$return .= $this->output->box_start('generalbox webservicestokenui');
$return .= get_string('rsskeyshelp');
$table = new html_table();
$table->head = array($strtoken, $stroperation);
$table->align = array('left', 'center');
$table->width = '100%';
$table->data = array();
if (!empty($token)) {
$reset = html_writer::link(new moodle_url('/user/managetoken.php', [
'action' => 'resetrsstoken',
]), get_string('reset'));
$table->data[] = array($token, $reset);
$return .= html_writer::table($table);
} else {
$return .= get_string('notoken', 'webservice');
}
$return .= $this->output->box_end();
return $return;
}
}
+219
View File
@@ -0,0 +1,219 @@
<?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/>.
/**
* Base class for unit tests for core_rss.
*
* @package core_rss
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_rss\privacy;
defined('MOODLE_INTERNAL') || die();
use core_privacy\tests\provider_testcase;
use core_rss\privacy\provider;
use core_privacy\local\request\writer;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
/**
* Unit tests for rss\classes\privacy\provider.php
*
* @copyright 2018 Sara Arjona <sara@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends provider_testcase {
/**
* Basic setup for these tests.
*/
public function setUp(): void {
$this->resetAfterTest(true);
}
/**
* Test getting the context for the user ID related to this plugin.
*/
public function test_get_contexts_for_userid(): void {
// Create user and RSS user keys.
$user = $this->getDataGenerator()->create_user();
$context = \context_user::instance($user->id);
$key = get_user_key('rss', $user->id);
$contextlist = provider::get_contexts_for_userid($user->id);
$this->assertEquals($context->id, $contextlist->current()->id);
}
/**
* Test that data is exported correctly for this plugin.
*/
public function test_export_user_data(): void {
global $DB;
// Create user and RSS user keys.
$user = $this->getDataGenerator()->create_user();
$context = \context_user::instance($user->id);
$keyvalue = get_user_key('rss', $user->id);
$key = $DB->get_record('user_private_key', ['value' => $keyvalue]);
// Validate exported data.
$this->setUser($user);
/** @var \core_privacy\tests\request\content_writer $writer */
$writer = writer::with_context($context);
$this->assertFalse($writer->has_any_data());
$this->export_context_data_for_user($user->id, $context, 'core_rss');
$userkeydata = $writer->get_related_data([], 'userkeys');
$this->assertCount(1, $userkeydata->keys);
$this->assertEquals($key->script, reset($userkeydata->keys)->script);
}
/**
* Test for provider::delete_data_for_all_users_in_context().
*/
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
// Create user and RSS user keys.
$user = $this->getDataGenerator()->create_user();
$context = \context_user::instance($user->id);
$keyvalue = get_user_key('rss', $user->id);
$key = $DB->get_record('user_private_key', ['value' => $keyvalue]);
// Before deletion, we should have 1 user_private_key.
$count = $DB->count_records('user_private_key', ['script' => 'rss']);
$this->assertEquals(1, $count);
// Delete data.
provider::delete_data_for_all_users_in_context($context);
// After deletion, the user_private_key entries should have been deleted.
$count = $DB->count_records('user_private_key', ['script' => 'rss']);
$this->assertEquals(0, $count);
}
/**
* Test for provider::delete_data_for_user().
*/
public function test_delete_data_for_user(): void {
global $DB;
// Create user and RSS user keys.
$user = $this->getDataGenerator()->create_user();
$context = \context_user::instance($user->id);
$keyvalue = get_user_key('rss', $user->id);
$key = $DB->get_record('user_private_key', ['value' => $keyvalue]);
// Before deletion, we should have 1 user_private_key.
$count = $DB->count_records('user_private_key', ['script' => 'rss']);
$this->assertEquals(1, $count);
// Delete data.
$contextlist = provider::get_contexts_for_userid($user->id);
$approvedcontextlist = new approved_contextlist($user, 'rss', $contextlist->get_contextids());
provider::delete_data_for_user($approvedcontextlist);
// After deletion, the user_private_key entries should have been deleted.
$count = $DB->count_records('user_private_key', ['script' => 'rss']);
$this->assertEquals(0, $count);
}
/**
* Test that only users with a user context are fetched.
*/
public function test_get_users_in_context(): void {
$component = 'core_rss';
// Create a user.
$user = $this->getDataGenerator()->create_user();
$usercontext = \context_user::instance($user->id);
$userlist = new \core_privacy\local\request\userlist($usercontext, $component);
// The list of users should not return anything yet (related data still haven't been created).
provider::get_users_in_context($userlist);
$this->assertCount(0, $userlist);
// Create private access key for user.
get_user_key('rss', $user->id);
// The list of users for user context should return the user.
provider::get_users_in_context($userlist);
$this->assertCount(1, $userlist);
$expected = [$user->id];
$actual = $userlist->get_userids();
$this->assertEquals($expected, $actual);
// The list of users for system context should not return any users.
$systemcontext = \context_system::instance();
$userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
provider::get_users_in_context($userlist);
$this->assertCount(0, $userlist);
}
/**
* Test that data for users in approved userlist is deleted.
*/
public function test_delete_data_for_users(): void {
$component = 'core_rss';
// Create a user1.
$user1 = $this->getDataGenerator()->create_user();
$usercontext1 = \context_user::instance($user1->id);
// Create list of users with a related user data in usercontext1.
$userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component);
// Create a user1.
$user2 = $this->getDataGenerator()->create_user();
$usercontext2 = \context_user::instance($user2->id);
// Create list of users with a related user data in usercontext2.
$userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
// Create private access key for user1.
get_user_key('rss', $user1->id);
// Create private access key for user2.
get_user_key('rss', $user2->id);
// Ensure the user list for usercontext1 contains user1.
provider::get_users_in_context($userlist1);
$this->assertCount(1, $userlist1);
// Ensure the user list for usercontext2 contains user2.
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
// Convert $userlist1 into an approved_contextlist.
$approvedlist = new approved_userlist($usercontext1, $component, $userlist1->get_userids());
// Delete using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in usercontext1 - The user list should now be empty.
$userlist1 = new \core_privacy\local\request\userlist($usercontext1, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(0, $userlist1);
// Re-fetch users in usercontext2 - The user list should not be empty (user2).
$userlist2 = new \core_privacy\local\request\userlist($usercontext2, $component);
provider::get_users_in_context($userlist2);
$this->assertCount(1, $userlist2);
// User data should be only removed in the user context.
$systemcontext = \context_system::instance();
// Add userlist2 to the approved user list in the system context.
$approvedlist = new approved_userlist($systemcontext, $component, $userlist2->get_userids());
// Delete user1 data using delete_data_for_user.
provider::delete_data_for_users($approvedlist);
// Re-fetch users in usercontext2 - The user list should not be empty (user2).
$userlist1 = new \core_privacy\local\request\userlist($usercontext2, $component);
provider::get_users_in_context($userlist1);
$this->assertCount(1, $userlist1);
}
}