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
+278
View File
@@ -0,0 +1,278 @@
<?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/>.
/**
* Wrapper script redirecting user operations to correct destination.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once("../config.php");
require_once($CFG->dirroot . '/course/lib.php');
$formaction = required_param('formaction', PARAM_LOCALURL);
$id = required_param('id', PARAM_INT);
$PAGE->set_url('/user/action_redir.php', array('formaction' => $formaction, 'id' => $id));
list($formaction) = explode('?', $formaction, 2);
// This page now only handles the bulk enrolment change actions, other actions are done with ajax.
$actions = array('bulkchange.php');
if (array_search($formaction, $actions) === false) {
throw new \moodle_exception('unknownuseraction');
}
if (!confirm_sesskey()) {
throw new \moodle_exception('confirmsesskeybad');
}
if ($formaction == 'bulkchange.php') {
// Backwards compatibility for enrolment plugins bulk change functionality.
// This awful code is adapting from the participant page with it's param names and values
// to the values expected by the bulk enrolment changes forms.
$formaction = required_param('formaction', PARAM_URL);
require_once($CFG->dirroot . '/enrol/locallib.php');
$url = new moodle_url($formaction);
// Get the enrolment plugin type and bulk action from the url.
$plugin = $url->param('plugin');
$operationname = $url->param('operation');
$dataformat = $url->param('dataformat');
$course = $DB->get_record('course', array('id' => $id), '*', MUST_EXIST);
$context = context_course::instance($id);
$PAGE->set_context($context);
$userids = optional_param_array('userid', array(), PARAM_INT);
$default = new moodle_url('/user/index.php', ['id' => $course->id]);
$returnurl = new moodle_url(optional_param('returnto', $default, PARAM_LOCALURL));
if (empty($userids)) {
$userids = optional_param_array('bulkuser', array(), PARAM_INT);
}
if (empty($userids)) {
// The first time list hack.
if (empty($userids) and $post = data_submitted()) {
foreach ($post as $k => $v) {
if (preg_match('/^user(\d+)$/', $k, $m)) {
$userids[] = $m[1];
}
}
}
}
if (empty($plugin) AND $operationname == 'download_participants') {
// Check permissions.
$pagecontext = ($course->id == SITEID) ? context_system::instance() : $context;
if (course_can_view_participants($pagecontext)) {
$plugins = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
if (isset($plugins[$dataformat])) {
if ($plugins[$dataformat]->is_enabled()) {
if (empty($userids)) {
redirect($returnurl, get_string('noselectedusers', 'bulkusers'));
}
$columnnames = array(
'firstname' => get_string('firstname'),
'lastname' => get_string('lastname'),
);
// Get the list of fields we have to hide.
$hiddenfields = [];
if (!has_capability('moodle/course:viewhiddenuserfields', $context)) {
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
}
// Retrieve all identity fields required for users.
$userfieldsapi = \core_user\fields::for_identity($context);
$userfields = $userfieldsapi->get_sql('u', true);
$identityfields = array_keys($userfields->mappings);
foreach ($identityfields as $field) {
$columnnames[$field] = \core_user\fields::get_display_name($field);
}
// Ensure users are enrolled in this course context, further limiting them by selected userids.
[$enrolledsql, $enrolledparams] = get_enrolled_sql($context);
[$useridsql, $useridparams] = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid');
[$userordersql, $userorderparams] = users_order_by_sql('u', null, $context);
$params = array_merge($userfields->params, $enrolledparams, $useridparams, $userorderparams);
// If user can only view their own groups then they can only export users from those groups too.
$groupmode = groups_get_course_groupmode($course);
if ($groupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $context)) {
$groups = groups_get_all_groups($course->id, $USER->id, 0, 'g.id');
$groupids = array_column($groups, 'id');
[$groupmembersql, $groupmemberparams] = groups_get_members_ids_sql($groupids, $context);
$params = array_merge($params, $groupmemberparams);
$groupmemberjoin = "JOIN ({$groupmembersql}) jg ON jg.id = u.id";
} else {
$groupmemberjoin = '';
}
// Add column for groups if the user can view them.
if (!isset($hiddenfields['groups'])) {
$columnnames['groupnames'] = get_string('groups');
$userfields->selects .= ', gcn.groupnames';
[$groupconcatnamesql, $groupconcatnameparams] = groups_get_names_concat_sql($course->id);
$groupconcatjoin = "LEFT JOIN ({$groupconcatnamesql}) gcn ON gcn.userid = u.id";
$params = array_merge($params, $groupconcatnameparams);
} else {
$groupconcatjoin = '';
}
$sql = "SELECT u.firstname, u.lastname {$userfields->selects}
FROM {user} u
{$userfields->joins}
JOIN ({$enrolledsql}) je ON je.id = u.id
{$groupmemberjoin}
{$groupconcatjoin}
WHERE u.id {$useridsql}
ORDER BY {$userordersql}";
$rs = $DB->get_recordset_sql($sql, $params);
// Provide callback to pre-process all records ensuring user identity fields are escaped if HTML supported.
\core\dataformat::download_data(
'courseid_' . $course->id . '_participants',
$dataformat,
$columnnames,
$rs,
function(stdClass $record, bool $supportshtml) use ($identityfields): stdClass {
if ($supportshtml) {
foreach ($identityfields as $identityfield) {
$record->{$identityfield} = s($record->{$identityfield});
}
}
return $record;
}
);
$rs->close();
}
}
}
} else {
$instances = enrol_get_instances($course->id, false);
$instance = false;
foreach ($instances as $oneinstance) {
if ($oneinstance->enrol == $plugin) {
$instance = $oneinstance;
break;
}
}
if (!$instance) {
throw new \moodle_exception('errorwithbulkoperation', 'enrol');
}
$manager = new course_enrolment_manager($PAGE, $course, $instance->id);
$plugins = $manager->get_enrolment_plugins();
if (!isset($plugins[$plugin])) {
throw new \moodle_exception('errorwithbulkoperation', 'enrol');
}
$plugin = $plugins[$plugin];
$operations = $plugin->get_bulk_operations($manager);
if (!isset($operations[$operationname])) {
throw new \moodle_exception('errorwithbulkoperation', 'enrol');
}
$operation = $operations[$operationname];
if (empty($userids)) {
redirect($returnurl, get_string('noselectedusers', 'bulkusers'));
}
$users = $manager->get_users_enrolments($userids);
$removed = array_diff($userids, array_keys($users));
if (!empty($removed)) {
// This manager does not filter by enrolment method - so we can get the removed users details.
$removedmanager = new course_enrolment_manager($PAGE, $course);
$removedusers = $removedmanager->get_users_enrolments($removed);
foreach ($removedusers as $removeduser) {
$msg = get_string('userremovedfromselectiona', 'enrol', fullname($removeduser));
\core\notification::warning($msg);
}
}
// We may have users from any kind of enrolment, we need to filter for the enrolment plugin matching the bulk action.
$matchesplugin = function($user) use ($plugin) {
foreach ($user->enrolments as $enrolment) {
if ($enrolment->enrolmentplugin->get_name() == $plugin->get_name()) {
return true;
}
}
return false;
};
$filteredusers = array_filter($users, $matchesplugin);
// If the bulk operation is deleting enrolments, we exclude in any case the current user as it was probably a mistake.
if ($operationname === 'deleteselectedusers' && array_key_exists($USER->id, $filteredusers)) {
\core\notification::warning(get_string('userremovedfromselectiona', 'enrol', fullname($USER)));
unset($filteredusers[$USER->id]);
}
if (empty($filteredusers)) {
redirect($returnurl, get_string('noselectedusers', 'bulkusers'));
}
$users = $filteredusers;
// Get the form for the bulk operation.
$mform = $operation->get_form($PAGE->url, array('users' => $users));
// If the mform is false then attempt an immediate process. This may be an immediate action that
// doesn't require user input OR confirmation.... who know what but maybe one day.
if ($mform === false) {
if ($operation->process($manager, $users, new stdClass)) {
redirect($returnurl);
} else {
throw new \moodle_exception('errorwithbulkoperation', 'enrol');
}
}
// Check if the bulk operation has been cancelled.
if ($mform->is_cancelled()) {
redirect($returnurl);
}
if ($mform->is_submitted() && $mform->is_validated() && confirm_sesskey()) {
if ($operation->process($manager, $users, $mform->get_data())) {
redirect($returnurl);
}
}
$pagetitle = get_string('bulkuseroperation', 'enrol');
$PAGE->set_title($pagetitle);
$PAGE->set_heading($pagetitle);
echo $OUTPUT->header();
echo $OUTPUT->heading($operation->get_title());
$mform->display();
echo $OUTPUT->footer();
exit();
}
} else {
throw new coding_exception('invalidaction');
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("core_user/edit_profile_fields",["exports","core_form/modalform","core/str"],(function(_exports,_modalform,_str){var obj;
/**
* User profile fields editor
*
* @module core_user/edit_profile_fields
* @copyright 2021 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modalform=(obj=_modalform)&&obj.__esModule?obj:{default:obj};const Selectors_actions={editCategory:'[data-action="editcategory"]',editField:'[data-action="editfield"]',createField:'[data-action="createfield"]'};_exports.init=()=>{document.addEventListener("click",(function(e){let element=e.target.closest(Selectors_actions.editCategory);if(element){e.preventDefault();const title=element.getAttribute("data-id")?(0,_str.getString)("profileeditcategory","admin",element.getAttribute("data-name")):(0,_str.getString)("profilecreatenewcategory","admin"),form=new _modalform.default({formClass:"core_user\\form\\profile_category_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:title},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}if(element=e.target.closest(Selectors_actions.editField),element){e.preventDefault();const form=new _modalform.default({formClass:"core_user\\form\\profile_field_form",args:{id:element.getAttribute("data-id")},modalConfig:{title:(0,_str.getString)("profileeditfield","admin",element.getAttribute("data-name"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}if(element=e.target.closest(Selectors_actions.createField),element){e.preventDefault();const form=new _modalform.default({formClass:"core_user\\form\\profile_field_form",args:{datatype:element.getAttribute("data-datatype"),categoryid:element.getAttribute("data-categoryid")},modalConfig:{title:(0,_str.getString)("profilecreatenewfield","admin",element.getAttribute("data-datatypename"))},returnFocus:element});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}}))}}));
//# sourceMappingURL=edit_profile_fields.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"edit_profile_fields.min.js","sources":["../src/edit_profile_fields.js"],"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\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\n\n/**\n * User profile fields editor\n *\n * @module core_user/edit_profile_fields\n * @copyright 2021 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n actions: {\n editCategory: '[data-action=\"editcategory\"]',\n editField: '[data-action=\"editfield\"]',\n createField: '[data-action=\"createfield\"]',\n },\n};\n\nexport const init = () => {\n document.addEventListener('click', function(e) {\n let element = e.target.closest(Selectors.actions.editCategory);\n if (element) {\n e.preventDefault();\n const title = element.getAttribute('data-id') ?\n getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :\n getString('profilecreatenewcategory', 'admin');\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_category_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.editField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {id: element.getAttribute('data-id')},\n modalConfig: {title: getString('profileeditfield', 'admin', element.getAttribute('data-name'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n\n element = e.target.closest(Selectors.actions.createField);\n if (element) {\n e.preventDefault();\n const form = new ModalForm({\n formClass: 'core_user\\\\form\\\\profile_field_form',\n args: {datatype: element.getAttribute('data-datatype'), categoryid: element.getAttribute('data-categoryid')},\n modalConfig: {title: getString('profilecreatenewfield', 'admin', element.getAttribute('data-datatypename'))},\n returnFocus: element,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n }\n });\n};\n"],"names":["Selectors","editCategory","editField","createField","document","addEventListener","e","element","target","closest","preventDefault","title","getAttribute","form","ModalForm","formClass","args","id","modalConfig","returnFocus","events","FORM_SUBMITTED","window","location","reload","show","datatype","categoryid"],"mappings":";;;;;;;sJA0BMA,kBACO,CACLC,aAAc,+BACdC,UAAW,4BACXC,YAAa,6CAID,KAChBC,SAASC,iBAAiB,SAAS,SAASC,OACpCC,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBC,iBAC7CM,QAAS,CACTD,EAAEI,uBACIC,MAAQJ,QAAQK,aAAa,YAC/B,kBAAU,sBAAuB,QAASL,QAAQK,aAAa,eAC/D,kBAAU,2BAA4B,SACpCC,KAAO,IAAIC,mBAAU,CACvBC,UAAW,yCACXC,KAAM,CAACC,GAAIV,QAAQK,aAAa,YAChCM,YAAa,CAACP,MAAAA,OACdQ,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY,UAGTlB,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBE,WACzCK,QAAS,CACTD,EAAEI,uBACIG,KAAO,IAAIC,mBAAU,CACvBC,UAAW,sCACXC,KAAM,CAACC,GAAIV,QAAQK,aAAa,YAChCM,YAAa,CAACP,OAAO,kBAAU,mBAAoB,QAASJ,QAAQK,aAAa,eACjFO,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY,UAGTlB,QAAUD,EAAEE,OAAOC,QAAQT,kBAAkBG,aACzCI,QAAS,CACTD,EAAEI,uBACIG,KAAO,IAAIC,mBAAU,CACvBC,UAAW,sCACXC,KAAM,CAACU,SAAUnB,QAAQK,aAAa,iBAAkBe,WAAYpB,QAAQK,aAAa,oBACzFM,YAAa,CAACP,OAAO,kBAAU,wBAAyB,QAASJ,QAAQK,aAAa,uBACtFO,YAAaZ,UAEjBM,KAAKR,iBAAiBQ,KAAKO,OAAOC,gBAAgB,IAAMC,OAAOC,SAASC,WACxEX,KAAKY"}
+10
View File
@@ -0,0 +1,10 @@
define("core_user/form_user_selector",["exports","core/ajax","core/templates","core/str"],(function(_exports,_ajax,_templates,_str){var obj;
/**
* Provides the required functionality for an autocomplete element to select a user.
*
* @module core_user/form_user_selector
* @copyright 2020 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.processResults=function(selector,results){return Array.isArray(results)?results.map((result=>({value:result.id,label:result.label}))):results},_exports.transport=async function(selector,query,callback,failure){const request={methodname:"core_user_search_identity",args:{query:query}};try{const response=await _ajax.default.call([request])[0];if(response.overflow){const msg=await(0,_str.getString)("toomanyuserstoshow","core",">"+response.maxusersperpage);callback(msg)}else{let labels=[];response.list.forEach((user=>{labels.push((0,_templates.render)("core_user/form_user_selector_suggestion",user))})),labels=await Promise.all(labels),response.list.forEach(((user,index)=>{user.label=labels[index]})),callback(response.list)}}catch(e){failure(e)}},_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj}}));
//# sourceMappingURL=form_user_selector.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"form_user_selector.min.js","sources":["../src/form_user_selector.js"],"sourcesContent":["// This file is part of Moodle - https://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 * Provides the required functionality for an autocomplete element to select a user.\n *\n * @module core_user/form_user_selector\n * @copyright 2020 David Mudrák <david@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport {render as renderTemplate} from 'core/templates';\nimport {getString} from 'core/str';\n\n/**\n * Load the list of users matching the query and render the selector labels for them.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {String} query The query string.\n * @param {Function} callback A callback function receiving an array of results.\n * @param {Function} failure A function to call in case of failure, receiving the error message.\n */\nexport async function transport(selector, query, callback, failure) {\n\n const request = {\n methodname: 'core_user_search_identity',\n args: {\n query: query\n }\n };\n\n try {\n const response = await Ajax.call([request])[0];\n\n if (response.overflow) {\n const msg = await getString('toomanyuserstoshow', 'core', '>' + response.maxusersperpage);\n callback(msg);\n\n } else {\n let labels = [];\n response.list.forEach(user => {\n labels.push(renderTemplate('core_user/form_user_selector_suggestion', user));\n });\n labels = await Promise.all(labels);\n\n response.list.forEach((user, index) => {\n user.label = labels[index];\n });\n\n callback(response.list);\n }\n\n } catch (e) {\n failure(e);\n }\n}\n\n/**\n * Process the results for auto complete elements.\n *\n * @param {String} selector The selector of the auto complete element.\n * @param {Array} results An array or results returned by {@see transport()}.\n * @return {Array} New array of the selector options.\n */\nexport function processResults(selector, results) {\n\n if (!Array.isArray(results)) {\n return results;\n\n } else {\n return results.map(result => ({value: result.id, label: result.label}));\n }\n}\n"],"names":["selector","results","Array","isArray","map","result","value","id","label","query","callback","failure","request","methodname","args","response","Ajax","call","overflow","msg","maxusersperpage","labels","list","forEach","user","push","Promise","all","index","e"],"mappings":";;;;;;;8FA6E+BA,SAAUC,gBAEhCC,MAAMC,QAAQF,SAIRA,QAAQG,KAAIC,UAAYC,MAAOD,OAAOE,GAAIC,MAAOH,OAAOG,UAHxDP,2CA7CiBD,SAAUS,MAAOC,SAAUC,eAEjDC,QAAU,CACZC,WAAY,4BACZC,KAAM,CACFL,MAAOA,kBAKLM,eAAiBC,cAAKC,KAAK,CAACL,UAAU,MAExCG,SAASG,SAAU,OACbC,UAAY,kBAAU,qBAAsB,OAAQ,IAAMJ,SAASK,iBACzEV,SAASS,SAEN,KACCE,OAAS,GACbN,SAASO,KAAKC,SAAQC,OAClBH,OAAOI,MAAK,qBAAe,0CAA2CD,UAE1EH,aAAeK,QAAQC,IAAIN,QAE3BN,SAASO,KAAKC,SAAQ,CAACC,KAAMI,SACzBJ,KAAKhB,MAAQa,OAAOO,UAGxBlB,SAASK,SAASO,OAGxB,MAAOO,GACLlB,QAAQkB"}
+10
View File
@@ -0,0 +1,10 @@
define("core_user/local/participants/bulkactions",["exports","core_user/repository","core/str","core/modal_events","core/modal_save_cancel","core/notification","core/templates","core/toast"],(function(_exports,Repository,Str,_modal_events,_modal_save_cancel,_notification,_templates,_toast){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
/**
* Bulk actions for lists of participants.
*
* @module core_user/local/participants/bulkactions
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showSendMessage=_exports.showAddNote=void 0,Repository=_interopRequireWildcard(Repository),Str=_interopRequireWildcard(Str),_modal_events=_interopRequireDefault(_modal_events),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);_exports.showAddNote=(courseid,users,noteStateNames,stateHelpIcon)=>{if(!users.length)return Promise.resolve();const states=[];for(let key in noteStateNames)switch(key){case"draft":states.push({value:"personal",label:noteStateNames[key]});break;case"public":states.push({value:"course",label:noteStateNames[key],selected:1});break;case"site":states.push({value:key,label:noteStateNames[key]})}const context={stateNames:states,stateHelpIcon:stateHelpIcon.innerHTML};let titlePromise=null;return titlePromise=1===users.length?Str.get_string("addbulknotesingle","core_notes"):Str.get_string("addbulknote","core_notes",users.length),_modal_save_cancel.default.create({body:_templates.default.render("core_user/add_bulk_note",context),title:titlePromise,buttons:{save:titlePromise},removeOnClose:!0,show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(()=>submitAddNote(courseid,users,modal))),modal)))};const submitAddNote=(courseid,users,modal)=>{const text=modal.getRoot().find("form textarea").val(),publishstate=modal.getRoot().find("form select").val(),notes=users.map((userid=>({userid:userid,text:text,courseid:courseid,publishstate:publishstate})));return Repository.createNotesForUsers(notes).then((noteIds=>1===noteIds.length?Str.get_string("addbulknotedonesingle","core_notes"):Str.get_string("addbulknotedone","core_notes",noteIds.length))).then((msg=>(0,_toast.add)(msg))).catch(_notification.default.exception)};_exports.showSendMessage=users=>{if(!users.length)return Promise.resolve();let titlePromise;return titlePromise=1===users.length?Str.get_string("sendbulkmessagesingle","core_message"):Str.get_string("sendbulkmessage","core_message",users.length),_modal_save_cancel.default.create({body:_templates.default.render("core_user/send_bulk_message",{}),title:titlePromise,buttons:{save:titlePromise},removeOnClose:!0,show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(e=>{const text=modal.getRoot().find("form textarea").val();if(""===text.trim())return modal.getRoot().find('[data-role="messagetextrequired"]').removeAttr("hidden"),void e.preventDefault();submitSendMessage(modal,users,text)})),modal)))};const submitSendMessage=(modal,users,text)=>{const messages=users.map((touserid=>({touserid:touserid,text:text})));return Repository.sendMessagesToUsers(messages).then((messageIds=>1==messageIds.length?Str.get_string("sendbulkmessagesentsingle","core_message"):Str.get_string("sendbulkmessagesent","core_message",messageIds.length))).then((msg=>(0,_toast.add)(msg))).catch(_notification.default.exception)}}));
//# sourceMappingURL=bulkactions.min.js.map
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
define("core_user/participants",["exports","core_table/dynamic","core/str","core/checkbox-toggleall","core/custom_interaction_events","core_table/local/dynamic/selectors","core/modal_events","core/notification","core/pending","jquery","core_user/local/participants/bulkactions","core/inplace_editable"],(function(_exports,DynamicTable,Str,_checkboxToggleall,_custom_interaction_events,_selectors,_modal_events,_notification,_pending,_jquery,_bulkactions,_inplace_editable){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
/**
* Some UI stuff for participants page.
* This is also used by the report/participants/index.php because it has the same functionality.
*
* @module core_user/participants
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,DynamicTable=_interopRequireWildcard(DynamicTable),Str=_interopRequireWildcard(Str),_checkboxToggleall=_interopRequireDefault(_checkboxToggleall),_custom_interaction_events=_interopRequireDefault(_custom_interaction_events),_selectors=_interopRequireDefault(_selectors),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_jquery=_interopRequireDefault(_jquery);const Selectors_bulkActionSelect="#formactionid",Selectors_bulkUserSelectedCheckBoxes="input[data-togglegroup='participants-table'][data-toggle='slave']:checked",Selectors_checkCountButton="#checkall",Selectors_showCountText='[data-region="participant-count"]',Selectors_stateHelpIcon='[data-region="state-help-icon"]',Selectors_tableForm=uniqueId=>'form[data-table-unique-id="'.concat(uniqueId,'"]');_exports.init=_ref=>{let{uniqueid:uniqueid,noteStateNames:noteStateNames={}}=_ref;const root=document.querySelector(Selectors_tableForm(uniqueid)),getTableFromUniqueId=uniqueId=>root.querySelector(_selectors.default.main.fromRegionId(uniqueId)),resetBulkAction=bulkActionSelect=>{bulkActionSelect.value=""};_custom_interaction_events.default.define(Selectors_bulkActionSelect,[_custom_interaction_events.default.events.accessibleChange]),(0,_jquery.default)(Selectors_bulkActionSelect).on(_custom_interaction_events.default.events.accessibleChange,(e=>{const bulkActionSelect=e.target.closest("select"),action=bulkActionSelect.value,checkboxes=getTableFromUniqueId(uniqueid).querySelectorAll(Selectors_bulkUserSelectedCheckBoxes),pendingPromise=new _pending.default("core_user/participants:bulkActionSelect");if(-1!==action.indexOf("#")){e.preventDefault();const ids=[];let bulkAction;if(checkboxes.forEach((checkbox=>{ids.push(checkbox.getAttribute("name").replace("user",""))})),"#messageselect"===action?bulkAction=(0,_bulkactions.showSendMessage)(ids):"#addgroupnote"===action&&(bulkAction=(0,_bulkactions.showAddNote)(root.dataset.courseId,ids,noteStateNames,root.querySelector(Selectors_stateHelpIcon))),bulkAction){const pendingBulkAction=new _pending.default("core_user/participants:bulkActionSelected");bulkAction.then((modal=>(modal.getRoot().on(_modal_events.default.hidden,(()=>{bulkActionSelect.focus()})),pendingBulkAction.resolve(),modal))).catch(_notification.default.exception)}}else""!==action&&checkboxes.length&&bulkActionSelect.form.submit();resetBulkAction(bulkActionSelect),pendingPromise.resolve()})),root.addEventListener("click",(e=>{const checkCountButton=root.querySelector(Selectors_checkCountButton);if(checkCountButton&&checkCountButton.contains(e.target)){e.preventDefault();const tableRoot=getTableFromUniqueId(uniqueid);DynamicTable.setPageSize(tableRoot,checkCountButton.dataset.targetPageSize).then((tableRoot=>(_checkboxToggleall.default.setGroupState(root,"participants-table",!0),tableRoot))).catch(_notification.default.exception)}})),root.addEventListener(DynamicTable.Events.tableContentRefreshed,(e=>{const checkCountButton=root.querySelector(Selectors_checkCountButton),tableRoot=e.target,defaultPageSize=parseInt(tableRoot.dataset.tableDefaultPerPage,10),currentPageSize=parseInt(tableRoot.dataset.tablePageSize,10),totalRowCount=parseInt(tableRoot.dataset.tableTotalRows,10);_checkboxToggleall.default.updateSlavesFromMasterState(root,"participants-table");const pageCountStrings=[{key:"countparticipantsfound",component:"core_user",param:totalRowCount}];totalRowCount<=defaultPageSize?checkCountButton&&checkCountButton.classList.add("hidden"):totalRowCount<=currentPageSize?(pageCountStrings.push({key:"selectalluserswithcount",component:"core",param:defaultPageSize}),checkCountButton&&checkCountButton.classList.add("hidden")):(pageCountStrings.push({key:"selectalluserswithcount",component:"core",param:totalRowCount}),checkCountButton&&checkCountButton.classList.remove("hidden")),Str.get_strings(pageCountStrings).then((_ref2=>{let[showingParticipantCountString,selectCountString]=_ref2;root.querySelector(Selectors_showCountText).innerHTML=showingParticipantCountString,selectCountString&&checkCountButton&&(checkCountButton.value=selectCountString)})).catch(_notification.default.exception)}))}}));
//# sourceMappingURL=participants.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("core_user/participants_filter",["exports","core/datafilter","core_table/dynamic","core/datafilter/selectors","core/notification","core/pending"],(function(_exports,_datafilter,DynamicTable,_selectors,_notification,_pending){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Participants filter management.
*
* @module core_user/participants_filter
* @copyright 2021 Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_datafilter=_interopRequireDefault(_datafilter),DynamicTable=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(DynamicTable),_selectors=_interopRequireDefault(_selectors),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);_exports.init=filterRegionId=>{const filterSet=document.getElementById(filterRegionId),coreFilter=new _datafilter.default(filterSet,(function(filters,pendingPromise){DynamicTable.setFilters(DynamicTable.getTableFromId(filterSet.dataset.tableRegion),{jointype:parseInt(filterSet.querySelector(_selectors.default.filterset.fields.join).value,10),filters:filters}).then((result=>(pendingPromise.resolve(),result))).catch(_notification.default.exception)}));coreFilter.init();const tableRoot=DynamicTable.getTableFromId(filterSet.dataset.tableRegion),initialFilters=DynamicTable.getFilters(tableRoot);if(initialFilters){const initialFilterPromise=new _pending.default("core/filter:setFilterFromConfig");(config=>{const filterConfig=Object.entries(config.filters);if(!filterConfig.length)return Promise.resolve();filterSet.querySelector(_selectors.default.filterset.fields.join).value=config.jointype;const filterPromises=filterConfig.map((_ref=>{let[filterType,filterData]=_ref;if("courseid"===filterType)return!1;const filterValues=filterData.values;return!!filterValues.length&&coreFilter.addFilterRow().then((_ref2=>{let[filterRow]=_ref2;coreFilter.addFilter(filterRow,filterType,filterValues)}))})).filter((promise=>promise));return filterPromises.length?Promise.all(filterPromises).then((()=>coreFilter.removeEmptyFilters())).then((()=>{coreFilter.updateFiltersOptions()})).then((()=>{coreFilter.updateTableFromFilter()})):Promise.resolve()})(initialFilters).then((()=>initialFilterPromise.resolve())).catch()}}}));
//# sourceMappingURL=participants_filter.min.js.map
File diff suppressed because one or more lines are too long
+10
View File
@@ -0,0 +1,10 @@
define("core_user/private_files",["exports","core_form/dynamicform","core_form/modalform","core/str","core/toast"],(function(_exports,_dynamicform,_modalform,_str,_toast){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
/**
* Module to handle AJAX interactions with user private files
*
* @module core_user/private_files
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.initModal=_exports.initDynamicForm=void 0,_dynamicform=_interopRequireDefault(_dynamicform),_modalform=_interopRequireDefault(_modalform);_exports.initDynamicForm=(containerSelector,formClass)=>{const form=new _dynamicform.default(document.querySelector(containerSelector),formClass);form.addEventListener(form.events.FORM_SUBMITTED,(()=>{form.load(),(0,_str.getString)("changessaved").then(_toast.add).catch(null)})),form.addEventListener(form.events.CANCEL_BUTTON_PRESSED,(()=>window.location.reload()))};_exports.initModal=(elementSelector,formClass)=>{document.querySelector(elementSelector).addEventListener("click",(function(e){e.preventDefault();const form=new _modalform.default({formClass:formClass,args:{nosubmit:!0},modalConfig:{title:(0,_str.getString)("privatefilesmanage")},returnFocus:e.target});form.addEventListener(form.events.FORM_SUBMITTED,(()=>window.location.reload())),form.show()}))}}));
//# sourceMappingURL=private_files.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"private_files.min.js","sources":["../src/private_files.js"],"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 * Module to handle AJAX interactions with user private files\n *\n * @module core_user/private_files\n * @copyright 2020 Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport DynamicForm from 'core_form/dynamicform';\nimport ModalForm from 'core_form/modalform';\nimport {getString} from 'core/str';\nimport {add as addToast} from 'core/toast';\n\n/**\n * Initialize private files form as AJAX form\n *\n * @param {String} containerSelector\n * @param {String} formClass\n */\nexport const initDynamicForm = (containerSelector, formClass) => {\n const form = new DynamicForm(document.querySelector(containerSelector), formClass);\n\n // When form is saved, refresh it to remove validation errors, if any:\n form.addEventListener(form.events.FORM_SUBMITTED, () => {\n form.load();\n getString('changessaved')\n .then(addToast)\n .catch(null);\n });\n\n // Reload the page on cancel.\n form.addEventListener(form.events.CANCEL_BUTTON_PRESSED, () => window.location.reload());\n};\n\n/**\n * Initialize private files form as Modal form\n *\n * @param {String} elementSelector\n * @param {String} formClass\n */\nexport const initModal = (elementSelector, formClass) => {\n document.querySelector(elementSelector).addEventListener('click', function(e) {\n e.preventDefault();\n const form = new ModalForm({\n formClass,\n args: {nosubmit: true},\n modalConfig: {title: getString('privatefilesmanage')},\n returnFocus: e.target,\n });\n form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());\n form.show();\n });\n};\n"],"names":["containerSelector","formClass","form","DynamicForm","document","querySelector","addEventListener","events","FORM_SUBMITTED","load","then","addToast","catch","CANCEL_BUTTON_PRESSED","window","location","reload","elementSelector","e","preventDefault","ModalForm","args","nosubmit","modalConfig","title","returnFocus","target","show"],"mappings":";;;;;;;yOAiC+B,CAACA,kBAAmBC,mBACzCC,KAAO,IAAIC,qBAAYC,SAASC,cAAcL,mBAAoBC,WAGxEC,KAAKI,iBAAiBJ,KAAKK,OAAOC,gBAAgB,KAC9CN,KAAKO,0BACK,gBACTC,KAAKC,YACLC,MAAM,SAIXV,KAAKI,iBAAiBJ,KAAKK,OAAOM,uBAAuB,IAAMC,OAAOC,SAASC,+BAS1D,CAACC,gBAAiBhB,aACvCG,SAASC,cAAcY,iBAAiBX,iBAAiB,SAAS,SAASY,GACvEA,EAAEC,uBACIjB,KAAO,IAAIkB,mBAAU,CACvBnB,UAAAA,UACAoB,KAAM,CAACC,UAAU,GACjBC,YAAa,CAACC,OAAO,kBAAU,uBAC/BC,YAAaP,EAAEQ,SAEnBxB,KAAKI,iBAAiBJ,KAAKK,OAAOC,gBAAgB,IAAMM,OAAOC,SAASC,WACxEd,KAAKyB"}
+3
View File
@@ -0,0 +1,3 @@
define("core_user/repository",["exports","core/ajax"],(function(_exports,_ajax){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.unenrolUser=_exports.submitUserEnrolmentForm=_exports.setUserPreferences=_exports.setUserPreference=_exports.sendMessagesToUsers=_exports.getUserPreferences=_exports.getUserPreference=_exports.createNotesForUsers=void 0;_exports.getUserPreference=function(name){let userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return getUserPreferences(name,userid).then((response=>response.preferences[0].value))};const getUserPreferences=function(){let name=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,userid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return(0,_ajax.call)([{methodname:"core_user_get_user_preferences",args:{name:name,userid:userid}}])[0]};_exports.getUserPreferences=getUserPreferences;_exports.setUserPreference=function(name){let value=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,userid=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return setUserPreferences([{name:name,value:value,userid:userid}])};const setUserPreferences=preferences=>(0,_ajax.call)([{methodname:"core_user_set_user_preferences",args:{preferences:preferences}}])[0];_exports.setUserPreferences=setUserPreferences;_exports.unenrolUser=userEnrolmentId=>(0,_ajax.call)([{methodname:"core_enrol_unenrol_user_enrolment",args:{ueid:userEnrolmentId}}])[0];_exports.submitUserEnrolmentForm=formdata=>(0,_ajax.call)([{methodname:"core_enrol_submit_user_enrolment_form",args:{formdata:formdata}}])[0];_exports.createNotesForUsers=notes=>(0,_ajax.call)([{methodname:"core_notes_create_notes",args:{notes:notes}}])[0];_exports.sendMessagesToUsers=messages=>(0,_ajax.call)([{methodname:"core_message_send_instant_messages",args:{messages:messages}}])[0]}));
//# sourceMappingURL=repository.min.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"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 * Module to handle AJAX interactions.\n *\n * @module core_user/repository\n * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\n\n/**\n * Get single user preference\n *\n * @param {String} name Name of the preference\n * @param {Number} userid User ID (defaults to current user)\n * @return {Promise}\n */\nexport const getUserPreference = (name, userid = 0) => {\n return getUserPreferences(name, userid)\n .then(response => response.preferences[0].value);\n};\n\n/**\n * Get multiple user preferences\n *\n * @param {String|null} name Name of the preference (omit if you want to retrieve all)\n * @param {Number} userid User ID (defaults to current user)\n * @return {Promise}\n */\nexport const getUserPreferences = (name = null, userid = 0) => {\n return fetchMany([{\n methodname: 'core_user_get_user_preferences',\n args: {name, userid}\n }])[0];\n};\n\n/**\n * Set single user preference\n *\n * @param {String} name Name of the preference\n * @param {String|null} value Value of the preference (omit if you want to remove the current value)\n * @param {Number} userid User ID (defaults to current user)\n * @return {Promise}\n */\nexport const setUserPreference = (name, value = null, userid = 0) => {\n return setUserPreferences([{name, value, userid}]);\n};\n\n/**\n * Set multiple user preferences\n *\n * @param {Object[]} preferences Array of preferences containing name/value/userid attributes\n * @return {Promise}\n */\nexport const setUserPreferences = (preferences) => {\n return fetchMany([{\n methodname: 'core_user_set_user_preferences',\n args: {preferences}\n }])[0];\n};\n\n/**\n * Unenrol the user with the specified user enrolmentid ID.\n *\n * @param {Number} userEnrolmentId\n * @return {Promise}\n */\nexport const unenrolUser = userEnrolmentId => {\n return fetchMany([{\n methodname: 'core_enrol_unenrol_user_enrolment',\n args: {\n ueid: userEnrolmentId,\n },\n }])[0];\n};\n\n/**\n * Submit the user enrolment form with the specified form data.\n *\n * @param {String} formdata\n * @return {Promise}\n */\nexport const submitUserEnrolmentForm = formdata => {\n return fetchMany([{\n methodname: 'core_enrol_submit_user_enrolment_form',\n args: {\n formdata,\n },\n }])[0];\n};\n\nexport const createNotesForUsers = notes => {\n return fetchMany([{\n methodname: 'core_notes_create_notes',\n args: {\n notes\n }\n }])[0];\n};\n\nexport const sendMessagesToUsers = messages => {\n return fetchMany([{\n methodname: 'core_message_send_instant_messages',\n args: {messages}\n }])[0];\n};\n"],"names":["name","userid","getUserPreferences","then","response","preferences","value","methodname","args","setUserPreferences","userEnrolmentId","ueid","formdata","notes","messages"],"mappings":"wYAgCiC,SAACA,UAAMC,8DAAS,SACtCC,mBAAmBF,KAAMC,QAC3BE,MAAKC,UAAYA,SAASC,YAAY,GAAGC,eAUrCJ,mBAAqB,eAACF,4DAAO,KAAMC,8DAAS,SAC9C,cAAU,CAAC,CACdM,WAAY,iCACZC,KAAM,CAACR,KAAAA,KAAMC,OAAAA,WACb,8EAWyB,SAACD,UAAMM,6DAAQ,KAAML,8DAAS,SACpDQ,mBAAmB,CAAC,CAACT,KAAAA,KAAMM,MAAAA,MAAOL,OAAAA,iBAShCQ,mBAAsBJ,cACxB,cAAU,CAAC,CACdE,WAAY,iCACZC,KAAM,CAACH,YAAAA,gBACP,uEASmBK,kBAChB,cAAU,CAAC,CACdH,WAAY,oCACZC,KAAM,CACFG,KAAMD,oBAEV,oCAS+BE,WAC5B,cAAU,CAAC,CACdL,WAAY,wCACZC,KAAM,CACFI,SAAAA,aAEJ,gCAG2BC,QACxB,cAAU,CAAC,CACdN,WAAY,0BACZC,KAAM,CACFK,MAAAA,UAEJ,gCAG2BC,WACxB,cAAU,CAAC,CACdP,WAAY,qCACZC,KAAM,CAACM,SAAAA,aACP"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+306
View File
@@ -0,0 +1,306 @@
// 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/>.
/**
* Allow the user to search for learners.
*
* @module core_user/comboboxsearch/user
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import search_combobox from 'core/comboboxsearch/search_combobox';
import {getStrings} from 'core/str';
import {renderForPromise, replaceNodeContents} from 'core/templates';
import $ from 'jquery';
export default class UserSearch extends search_combobox {
courseID;
groupID;
// A map of user profile field names that is human-readable.
profilestringmap = null;
constructor() {
super();
// Register a couple of events onto the document since we need to check if they are moving off the component.
['click', 'focus'].forEach(eventType => {
// Since we are handling dropdowns manually, ensure we can close it when moving off.
document.addEventListener(eventType, e => {
if (this.searchDropdown.classList.contains('show') && !this.combobox.contains(e.target)) {
this.toggleDropdown();
}
}, true);
});
// Register keyboard events.
this.component.addEventListener('keydown', this.keyHandler.bind(this));
// Define our standard lookups.
this.selectors = {...this.selectors,
courseid: '[data-region="courseid"]',
groupid: '[data-region="groupid"]',
resetPageButton: '[data-action="resetpage"]',
};
this.courseID = this.component.querySelector(this.selectors.courseid).dataset.courseid;
this.groupID = document.querySelector(this.selectors.groupid)?.dataset?.groupid;
this.instance = this.component.querySelector(this.selectors.instance).dataset.instance;
// We need to render some content by default for ARIA purposes.
this.renderDefault();
}
static init() {
return new UserSearch();
}
/**
* The overall div that contains the searching widget.
*
* @returns {string}
*/
componentSelector() {
return '.user-search';
}
/**
* The dropdown div that contains the searching widget result space.
*
* @returns {string}
*/
dropdownSelector() {
return '.usersearchdropdown';
}
/**
* Build the content then replace the node.
*/
async renderDropdown() {
const {html, js} = await renderForPromise('core_user/comboboxsearch/resultset', {
users: this.getMatchedResults().slice(0, 5),
hasresults: this.getMatchedResults().length > 0,
instance: this.instance,
matches: this.getMatchedResults().length,
searchterm: this.getSearchTerm(),
selectall: this.selectAllResultsLink(),
});
replaceNodeContents(this.getHTMLElements().searchDropdown, html, js);
// Remove aria-activedescendant when the available options change.
this.searchInput.removeAttribute('aria-activedescendant');
}
/**
* Build the content then replace the node by default we want our form to exist.
*/
async renderDefault() {
this.setMatchedResults(await this.filterDataset(await this.getDataset()));
this.filterMatchDataset();
await this.renderDropdown();
}
/**
* Get the data we will be searching against in this component.
*
* @returns {Promise<*>}
*/
fetchDataset() {
throw new Error(`fetchDataset() must be implemented in ${this.constructor.name}`);
}
/**
* Dictate to the search component how and what we want to match upon.
*
* @param {Array} filterableData
* @returns {Array} The users that match the given criteria.
*/
async filterDataset(filterableData) {
if (this.getPreppedSearchTerm()) {
const stringMap = await this.getStringMap();
return filterableData.filter((user) => Object.keys(user).some((key) => {
if (user[key] === "" || user[key] === null || !stringMap.get(key)) {
return false;
}
return user[key].toString().toLowerCase().includes(this.getPreppedSearchTerm());
}));
} else {
return [];
}
}
/**
* Given we have a subset of the dataset, set the field that we matched upon to inform the end user.
*
* @returns {Array} The results with the matched fields inserted.
*/
async filterMatchDataset() {
const stringMap = await this.getStringMap();
this.setMatchedResults(
this.getMatchedResults().map((user) => {
for (const [key, value] of Object.entries(user)) {
// Sometimes users have null values in their profile fields.
if (value === null) {
continue;
}
const valueString = value.toString().toLowerCase();
const preppedSearchTerm = this.getPreppedSearchTerm();
const searchTerm = this.getSearchTerm();
// Ensure we match only on expected keys.
const matchingFieldName = stringMap.get(key);
if (matchingFieldName && valueString.includes(preppedSearchTerm)) {
user.matchingFieldName = matchingFieldName;
// Safely prepare our matching results.
const escapedValueString = valueString.replace(/</g, '&lt;');
const escapedMatchingField = escapedValueString.replace(
preppedSearchTerm.replace(/</g, '&lt;'),
`<span class="font-weight-bold">${searchTerm.replace(/</g, '&lt;')}</span>`
);
if (user.email) {
user.matchingField = `${escapedMatchingField} (${user.email})`;
} else {
user.matchingField = escapedMatchingField;
}
break;
}
}
return user;
})
);
}
/**
* The handler for when a user changes the value of the component (selects an option from the dropdown).
*
* @param {Event} e The change event.
*/
changeHandler(e) {
this.toggleDropdown(); // Otherwise the dropdown stays open when user choose an option using keyboard.
if (e.target.value === '0') {
window.location = this.selectAllResultsLink();
} else {
window.location = this.selectOneLink(e.target.value);
}
}
/**
* The handler for when a user presses a key within the component.
*
* @param {KeyboardEvent} e The triggering event that we are working with.
*/
keyHandler(e) {
// Switch the key presses to handle keyboard nav.
switch (e.key) {
case 'ArrowUp':
case 'ArrowDown':
if (
this.getSearchTerm() !== ''
&& !this.searchDropdown.classList.contains('show')
&& e.target.contains(this.combobox)
) {
this.renderAndShow();
}
break;
case 'Enter':
case ' ':
if (e.target.closest(this.selectors.resetPageButton)) {
e.stopPropagation();
window.location = e.target.closest(this.selectors.resetPageButton).href;
break;
}
break;
case 'Escape':
this.toggleDropdown();
this.searchInput.focus({preventScroll: true});
break;
}
}
/**
* When called, hide or show the users dropdown.
*
* @param {Boolean} on Flag to toggle hiding or showing values.
*/
toggleDropdown(on = false) {
if (on) {
this.searchDropdown.classList.add('show');
$(this.searchDropdown).show();
this.getHTMLElements().searchInput.setAttribute('aria-expanded', 'true');
this.searchInput.focus({preventScroll: true});
} else {
this.searchDropdown.classList.remove('show');
$(this.searchDropdown).hide();
// As we are manually handling the dropdown, we need to do some housekeeping manually.
this.getHTMLElements().searchInput.setAttribute('aria-expanded', 'false');
this.searchInput.removeAttribute('aria-activedescendant');
this.searchDropdown.querySelectorAll('.active[role="option"]').forEach(option => {
option.classList.remove('active');
});
}
}
/**
* Build up the view all link.
*/
selectAllResultsLink() {
throw new Error(`selectAllResultsLink() must be implemented in ${this.constructor.name}`);
}
/**
* Build up the view all link that is dedicated to a particular result.
* We will call this function when a user interacts with the combobox to redirect them to show their results in the page.
*
* @param {Number} userID The ID of the user selected.
*/
selectOneLink(userID) {
throw new Error(`selectOneLink(${userID}) must be implemented in ${this.constructor.name}`);
}
/**
* Given the set of profile fields we can possibly search, fetch their strings,
* so we can report to screen readers the field that matched.
*
* @returns {Promise<void>}
*/
getStringMap() {
if (!this.profilestringmap) {
const requiredStrings = [
'username',
'fullname',
'firstname',
'lastname',
'email',
'city',
'country',
'department',
'institution',
'idnumber',
'phone1',
'phone2',
];
this.profilestringmap = getStrings(requiredStrings.map((key) => ({key})))
.then((stringArray) => new Map(
requiredStrings.map((key, index) => ([key, stringArray[index]]))
));
}
return this.profilestringmap;
}
}
+79
View File
@@ -0,0 +1,79 @@
// 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/>.
import ModalForm from 'core_form/modalform';
import {getString} from 'core/str';
/**
* User profile fields editor
*
* @module core_user/edit_profile_fields
* @copyright 2021 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
const Selectors = {
actions: {
editCategory: '[data-action="editcategory"]',
editField: '[data-action="editfield"]',
createField: '[data-action="createfield"]',
},
};
export const init = () => {
document.addEventListener('click', function(e) {
let element = e.target.closest(Selectors.actions.editCategory);
if (element) {
e.preventDefault();
const title = element.getAttribute('data-id') ?
getString('profileeditcategory', 'admin', element.getAttribute('data-name')) :
getString('profilecreatenewcategory', 'admin');
const form = new ModalForm({
formClass: 'core_user\\form\\profile_category_form',
args: {id: element.getAttribute('data-id')},
modalConfig: {title},
returnFocus: element,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
}
element = e.target.closest(Selectors.actions.editField);
if (element) {
e.preventDefault();
const form = new ModalForm({
formClass: 'core_user\\form\\profile_field_form',
args: {id: element.getAttribute('data-id')},
modalConfig: {title: getString('profileeditfield', 'admin', element.getAttribute('data-name'))},
returnFocus: element,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
}
element = e.target.closest(Selectors.actions.createField);
if (element) {
e.preventDefault();
const form = new ModalForm({
formClass: 'core_user\\form\\profile_field_form',
args: {datatype: element.getAttribute('data-datatype'), categoryid: element.getAttribute('data-categoryid')},
modalConfig: {title: getString('profilecreatenewfield', 'admin', element.getAttribute('data-datatypename'))},
returnFocus: element,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
}
});
};
+86
View File
@@ -0,0 +1,86 @@
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Provides the required functionality for an autocomplete element to select a user.
*
* @module core_user/form_user_selector
* @copyright 2020 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Ajax from 'core/ajax';
import {render as renderTemplate} from 'core/templates';
import {getString} from 'core/str';
/**
* Load the list of users matching the query and render the selector labels for them.
*
* @param {String} selector The selector of the auto complete element.
* @param {String} query The query string.
* @param {Function} callback A callback function receiving an array of results.
* @param {Function} failure A function to call in case of failure, receiving the error message.
*/
export async function transport(selector, query, callback, failure) {
const request = {
methodname: 'core_user_search_identity',
args: {
query: query
}
};
try {
const response = await Ajax.call([request])[0];
if (response.overflow) {
const msg = await getString('toomanyuserstoshow', 'core', '>' + response.maxusersperpage);
callback(msg);
} else {
let labels = [];
response.list.forEach(user => {
labels.push(renderTemplate('core_user/form_user_selector_suggestion', user));
});
labels = await Promise.all(labels);
response.list.forEach((user, index) => {
user.label = labels[index];
});
callback(response.list);
}
} catch (e) {
failure(e);
}
}
/**
* Process the results for auto complete elements.
*
* @param {String} selector The selector of the auto complete element.
* @param {Array} results An array or results returned by {@see transport()}.
* @return {Array} New array of the selector options.
*/
export function processResults(selector, results) {
if (!Array.isArray(results)) {
return results;
} else {
return results.map(result => ({value: result.id, label: result.label}));
}
}
@@ -0,0 +1,192 @@
// 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/>.
/**
* Bulk actions for lists of participants.
*
* @module core_user/local/participants/bulkactions
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as Repository from 'core_user/repository';
import * as Str from 'core/str';
import ModalEvents from 'core/modal_events';
import SaveCancelModal from 'core/modal_save_cancel';
import Notification from 'core/notification';
import Templates from 'core/templates';
import {add as notifyUser} from 'core/toast';
/**
* Show the add note popup
*
* @param {Number} courseid
* @param {Number[]} users
* @param {String[]} noteStateNames
* @param {HTMLElement} stateHelpIcon
* @return {Promise}
*/
export const showAddNote = (courseid, users, noteStateNames, stateHelpIcon) => {
if (!users.length) {
// No users were selected.
return Promise.resolve();
}
const states = [];
for (let key in noteStateNames) {
switch (key) {
case 'draft':
states.push({value: 'personal', label: noteStateNames[key]});
break;
case 'public':
states.push({value: 'course', label: noteStateNames[key], selected: 1});
break;
case 'site':
states.push({value: key, label: noteStateNames[key]});
break;
}
}
const context = {
stateNames: states,
stateHelpIcon: stateHelpIcon.innerHTML,
};
let titlePromise = null;
if (users.length === 1) {
titlePromise = Str.get_string('addbulknotesingle', 'core_notes');
} else {
titlePromise = Str.get_string('addbulknote', 'core_notes', users.length);
}
return SaveCancelModal.create({
body: Templates.render('core_user/add_bulk_note', context),
title: titlePromise,
buttons: {
save: titlePromise,
},
removeOnClose: true,
show: true,
})
.then(modal => {
modal.getRoot().on(ModalEvents.save, () => submitAddNote(courseid, users, modal));
return modal;
});
};
/**
* Add a note to this list of users.
*
* @param {Number} courseid
* @param {Number[]} users
* @param {Modal} modal
* @return {Promise}
*/
const submitAddNote = (courseid, users, modal) => {
const text = modal.getRoot().find('form textarea').val();
const publishstate = modal.getRoot().find('form select').val();
const notes = users.map(userid => {
return {
userid,
text,
courseid,
publishstate,
};
});
return Repository.createNotesForUsers(notes)
.then(noteIds => {
if (noteIds.length === 1) {
return Str.get_string('addbulknotedonesingle', 'core_notes');
} else {
return Str.get_string('addbulknotedone', 'core_notes', noteIds.length);
}
})
.then(msg => notifyUser(msg))
.catch(Notification.exception);
};
/**
* Show the send message popup.
*
* @param {Number[]} users
* @return {Promise}
*/
export const showSendMessage = users => {
if (!users.length) {
// Nothing to do.
return Promise.resolve();
}
let titlePromise;
if (users.length === 1) {
titlePromise = Str.get_string('sendbulkmessagesingle', 'core_message');
} else {
titlePromise = Str.get_string('sendbulkmessage', 'core_message', users.length);
}
return SaveCancelModal.create({
body: Templates.render('core_user/send_bulk_message', {}),
title: titlePromise,
buttons: {
save: titlePromise,
},
removeOnClose: true,
show: true,
})
.then(modal => {
modal.getRoot().on(ModalEvents.save, (e) => {
const text = modal.getRoot().find('form textarea').val();
if (text.trim() === '') {
modal.getRoot().find('[data-role="messagetextrequired"]').removeAttr('hidden');
e.preventDefault();
return;
}
submitSendMessage(modal, users, text);
});
return modal;
});
};
/**
* Send a message to these users.
*
* @param {Modal} modal
* @param {Number[]} users
* @param {String} text
* @return {Promise}
*/
const submitSendMessage = (modal, users, text) => {
const messages = users.map(touserid => {
return {
touserid,
text,
};
});
return Repository.sendMessagesToUsers(messages)
.then(messageIds => {
if (messageIds.length == 1) {
return Str.get_string('sendbulkmessagesentsingle', 'core_message');
} else {
return Str.get_string('sendbulkmessagesent', 'core_message', messageIds.length);
}
})
.then(msg => notifyUser(msg))
.catch(Notification.exception);
};
+200
View File
@@ -0,0 +1,200 @@
// 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/>.
/**
* Some UI stuff for participants page.
* This is also used by the report/participants/index.php because it has the same functionality.
*
* @module core_user/participants
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as DynamicTable from 'core_table/dynamic';
import * as Str from 'core/str';
import CheckboxToggleAll from 'core/checkbox-toggleall';
import CustomEvents from 'core/custom_interaction_events';
import DynamicTableSelectors from 'core_table/local/dynamic/selectors';
import ModalEvents from 'core/modal_events';
import Notification from 'core/notification';
import Pending from 'core/pending';
import jQuery from 'jquery';
import {showAddNote, showSendMessage} from 'core_user/local/participants/bulkactions';
import 'core/inplace_editable';
const Selectors = {
bulkActionSelect: "#formactionid",
bulkUserSelectedCheckBoxes: "input[data-togglegroup='participants-table'][data-toggle='slave']:checked",
checkCountButton: "#checkall",
showCountText: '[data-region="participant-count"]',
showCountToggle: '[data-action="showcount"]',
stateHelpIcon: '[data-region="state-help-icon"]',
tableForm: uniqueId => `form[data-table-unique-id="${uniqueId}"]`,
};
export const init = ({
uniqueid,
noteStateNames = {},
}) => {
const root = document.querySelector(Selectors.tableForm(uniqueid));
const getTableFromUniqueId = uniqueId => root.querySelector(DynamicTableSelectors.main.fromRegionId(uniqueId));
/**
* Private method.
*
* @method registerEventListeners
* @private
*/
const registerEventListeners = () => {
CustomEvents.define(Selectors.bulkActionSelect, [CustomEvents.events.accessibleChange]);
jQuery(Selectors.bulkActionSelect).on(CustomEvents.events.accessibleChange, e => {
const bulkActionSelect = e.target.closest('select');
const action = bulkActionSelect.value;
const tableRoot = getTableFromUniqueId(uniqueid);
const checkboxes = tableRoot.querySelectorAll(Selectors.bulkUserSelectedCheckBoxes);
const pendingPromise = new Pending('core_user/participants:bulkActionSelect');
if (action.indexOf('#') !== -1) {
e.preventDefault();
const ids = [];
checkboxes.forEach(checkbox => {
ids.push(checkbox.getAttribute('name').replace('user', ''));
});
let bulkAction;
if (action === '#messageselect') {
bulkAction = showSendMessage(ids);
} else if (action === '#addgroupnote') {
bulkAction = showAddNote(
root.dataset.courseId,
ids,
noteStateNames,
root.querySelector(Selectors.stateHelpIcon)
);
}
if (bulkAction) {
const pendingBulkAction = new Pending('core_user/participants:bulkActionSelected');
bulkAction
.then(modal => {
modal.getRoot().on(ModalEvents.hidden, () => {
// Focus on the action select when the dialog is closed.
bulkActionSelect.focus();
});
pendingBulkAction.resolve();
return modal;
})
.catch(Notification.exception);
}
} else if (action !== '' && checkboxes.length) {
bulkActionSelect.form.submit();
}
resetBulkAction(bulkActionSelect);
pendingPromise.resolve();
});
root.addEventListener('click', e => {
// Handle clicking of the "Select all" actions.
const checkCountButton = root.querySelector(Selectors.checkCountButton);
const checkCountButtonClicked = checkCountButton && checkCountButton.contains(e.target);
if (checkCountButtonClicked) {
e.preventDefault();
const tableRoot = getTableFromUniqueId(uniqueid);
DynamicTable.setPageSize(tableRoot, checkCountButton.dataset.targetPageSize)
.then(tableRoot => {
// Update the toggle state.
CheckboxToggleAll.setGroupState(root, 'participants-table', true);
return tableRoot;
})
.catch(Notification.exception);
}
});
// When the content is refreshed, update the row counts in various places.
root.addEventListener(DynamicTable.Events.tableContentRefreshed, e => {
const checkCountButton = root.querySelector(Selectors.checkCountButton);
const tableRoot = e.target;
const defaultPageSize = parseInt(tableRoot.dataset.tableDefaultPerPage, 10);
const currentPageSize = parseInt(tableRoot.dataset.tablePageSize, 10);
const totalRowCount = parseInt(tableRoot.dataset.tableTotalRows, 10);
CheckboxToggleAll.updateSlavesFromMasterState(root, 'participants-table');
const pageCountStrings = [
{
key: 'countparticipantsfound',
component: 'core_user',
param: totalRowCount,
},
];
if (totalRowCount <= defaultPageSize) {
if (checkCountButton) {
checkCountButton.classList.add('hidden');
}
} else if (totalRowCount <= currentPageSize) {
// The are fewer than the current page size.
pageCountStrings.push({
key: 'selectalluserswithcount',
component: 'core',
param: defaultPageSize,
});
if (checkCountButton) {
// The 'Check all [x]' button is only visible when there are values to set.
checkCountButton.classList.add('hidden');
}
} else {
pageCountStrings.push({
key: 'selectalluserswithcount',
component: 'core',
param: totalRowCount,
});
if (checkCountButton) {
checkCountButton.classList.remove('hidden');
}
}
Str.get_strings(pageCountStrings)
.then(([showingParticipantCountString, selectCountString]) => {
const showingParticipantCount = root.querySelector(Selectors.showCountText);
showingParticipantCount.innerHTML = showingParticipantCountString;
if (selectCountString && checkCountButton) {
checkCountButton.value = selectCountString;
}
return;
})
.catch(Notification.exception);
});
};
const resetBulkAction = bulkActionSelect => {
bulkActionSelect.value = '';
};
registerEventListeners();
};
+125
View File
@@ -0,0 +1,125 @@
// 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/>.
/**
* Participants filter management.
*
* @module core_user/participants_filter
* @copyright 2021 Tomo Tsuyuki <tomotsuyuki@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import CoreFilter from 'core/datafilter';
import * as DynamicTable from 'core_table/dynamic';
import Selectors from 'core/datafilter/selectors';
import Notification from 'core/notification';
import Pending from 'core/pending';
/**
* Initialise the participants filter on the element with the given id.
*
* @param {String} filterRegionId The id for the filter element.
*/
export const init = filterRegionId => {
const filterSet = document.getElementById(filterRegionId);
// Create and initialize filter.
const coreFilter = new CoreFilter(filterSet, function(filters, pendingPromise) {
DynamicTable.setFilters(
DynamicTable.getTableFromId(filterSet.dataset.tableRegion),
{
jointype: parseInt(filterSet.querySelector(Selectors.filterset.fields.join).value, 10),
filters,
}
)
.then(result => {
pendingPromise.resolve();
return result;
})
.catch(Notification.exception);
});
coreFilter.init();
/**
* Set the current filter options based on a provided configuration.
*
* @param {Object} config
* @param {Number} config.jointype
* @param {Object} config.filters
* @returns {Promise}
*/
const setFilterFromConfig = config => {
const filterConfig = Object.entries(config.filters);
if (!filterConfig.length) {
// There are no filters to set from.
return Promise.resolve();
}
// Set the main join type.
filterSet.querySelector(Selectors.filterset.fields.join).value = config.jointype;
const filterPromises = filterConfig.map(([filterType, filterData]) => {
if (filterType === 'courseid') {
// The courseid is a special case.
return false;
}
const filterValues = filterData.values;
if (!filterValues.length) {
// There are no values for this filter.
// Skip it.
return false;
}
return coreFilter.addFilterRow()
.then(([filterRow]) => {
coreFilter.addFilter(filterRow, filterType, filterValues);
return;
});
}).filter(promise => promise);
if (!filterPromises.length) {
return Promise.resolve();
}
return Promise.all(filterPromises)
.then(() => {
return coreFilter.removeEmptyFilters();
})
.then(() => {
coreFilter.updateFiltersOptions();
return;
})
.then(() => {
coreFilter.updateTableFromFilter();
return;
});
};
// Initialize DynamicTable for showing result.
const tableRoot = DynamicTable.getTableFromId(filterSet.dataset.tableRegion);
const initialFilters = DynamicTable.getFilters(tableRoot);
if (initialFilters) {
const initialFilterPromise = new Pending('core/filter:setFilterFromConfig');
// Apply the initial filter configuration.
setFilterFromConfig(initialFilters)
.then(() => initialFilterPromise.resolve())
.catch();
}
};
+67
View File
@@ -0,0 +1,67 @@
// 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/>.
/**
* Module to handle AJAX interactions with user private files
*
* @module core_user/private_files
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import DynamicForm from 'core_form/dynamicform';
import ModalForm from 'core_form/modalform';
import {getString} from 'core/str';
import {add as addToast} from 'core/toast';
/**
* Initialize private files form as AJAX form
*
* @param {String} containerSelector
* @param {String} formClass
*/
export const initDynamicForm = (containerSelector, formClass) => {
const form = new DynamicForm(document.querySelector(containerSelector), formClass);
// When form is saved, refresh it to remove validation errors, if any:
form.addEventListener(form.events.FORM_SUBMITTED, () => {
form.load();
getString('changessaved')
.then(addToast)
.catch(null);
});
// Reload the page on cancel.
form.addEventListener(form.events.CANCEL_BUTTON_PRESSED, () => window.location.reload());
};
/**
* Initialize private files form as Modal form
*
* @param {String} elementSelector
* @param {String} formClass
*/
export const initModal = (elementSelector, formClass) => {
document.querySelector(elementSelector).addEventListener('click', function(e) {
e.preventDefault();
const form = new ModalForm({
formClass,
args: {nosubmit: true},
modalConfig: {title: getString('privatefilesmanage')},
returnFocus: e.target,
});
form.addEventListener(form.events.FORM_SUBMITTED, () => window.location.reload());
form.show();
});
};
+121
View File
@@ -0,0 +1,121 @@
// 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/>.
/**
* Module to handle AJAX interactions.
*
* @module core_user/repository
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {call as fetchMany} from 'core/ajax';
/**
* Get single user preference
*
* @param {String} name Name of the preference
* @param {Number} userid User ID (defaults to current user)
* @return {Promise}
*/
export const getUserPreference = (name, userid = 0) => {
return getUserPreferences(name, userid)
.then(response => response.preferences[0].value);
};
/**
* Get multiple user preferences
*
* @param {String|null} name Name of the preference (omit if you want to retrieve all)
* @param {Number} userid User ID (defaults to current user)
* @return {Promise}
*/
export const getUserPreferences = (name = null, userid = 0) => {
return fetchMany([{
methodname: 'core_user_get_user_preferences',
args: {name, userid}
}])[0];
};
/**
* Set single user preference
*
* @param {String} name Name of the preference
* @param {String|null} value Value of the preference (omit if you want to remove the current value)
* @param {Number} userid User ID (defaults to current user)
* @return {Promise}
*/
export const setUserPreference = (name, value = null, userid = 0) => {
return setUserPreferences([{name, value, userid}]);
};
/**
* Set multiple user preferences
*
* @param {Object[]} preferences Array of preferences containing name/value/userid attributes
* @return {Promise}
*/
export const setUserPreferences = (preferences) => {
return fetchMany([{
methodname: 'core_user_set_user_preferences',
args: {preferences}
}])[0];
};
/**
* Unenrol the user with the specified user enrolmentid ID.
*
* @param {Number} userEnrolmentId
* @return {Promise}
*/
export const unenrolUser = userEnrolmentId => {
return fetchMany([{
methodname: 'core_enrol_unenrol_user_enrolment',
args: {
ueid: userEnrolmentId,
},
}])[0];
};
/**
* Submit the user enrolment form with the specified form data.
*
* @param {String} formdata
* @return {Promise}
*/
export const submitUserEnrolmentForm = formdata => {
return fetchMany([{
methodname: 'core_enrol_submit_user_enrolment_form',
args: {
formdata,
},
}])[0];
};
export const createNotesForUsers = notes => {
return fetchMany([{
methodname: 'core_notes_create_notes',
args: {
notes
}
}])[0];
};
export const sendMessagesToUsers = messages => {
return fetchMany([{
methodname: 'core_message_send_instant_messages',
args: {messages}
}])[0];
};
+362
View File
@@ -0,0 +1,362 @@
// 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/>.
/**
* AMD module for the user enrolment status field in the course participants page.
*
* @module core_user/status_field
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import * as DynamicTable from 'core_table/dynamic';
import * as Repository from './repository';
import * as Str from 'core/str';
import DynamicTableSelectors from 'core_table/local/dynamic/selectors';
import Fragment from 'core/fragment';
import ModalEvents from 'core/modal_events';
import Notification from 'core/notification';
import Templates from 'core/templates';
import {add as notifyUser} from 'core/toast';
import SaveCancelModal from 'core/modal_save_cancel';
import CancelModal from 'core/modal_cancel';
const Selectors = {
editEnrolment: '[data-action="editenrolment"]',
showDetails: '[data-action="showdetails"]',
unenrol: '[data-action="unenrol"]',
statusElement: '[data-status]',
};
/**
* Get the dynamic table from the specified link.
*
* @param {HTMLElement} link
* @returns {HTMLElement}
*/
const getDynamicTableFromLink = link => link.closest(DynamicTableSelectors.main.region);
/**
* Get the status container from the specified link.
*
* @param {HTMLElement} link
* @returns {HTMLElement}
*/
const getStatusContainer = link => link.closest(Selectors.statusElement);
/**
* Get user enrolment id from the specified link
*
* @param {HTMLElement} link
* @returns {Number}
*/
const getUserEnrolmentIdFromLink = link => link.getAttribute('rel');
/**
* Register all event listeners for the status fields.
*
* @param {Number} contextId
* @param {Number} uniqueId
*/
const registerEventListeners = (contextId, uniqueId) => {
const getBodyFunction = (userEnrolmentId, formData) => getBody(contextId, userEnrolmentId, formData);
document.addEventListener('click', e => {
const tableRoot = e.target.closest(DynamicTableSelectors.main.fromRegionId(uniqueId));
if (!tableRoot) {
return;
}
const editLink = e.target.closest(Selectors.editEnrolment);
if (editLink) {
e.preventDefault();
showEditDialogue(editLink, getBodyFunction);
}
const unenrolLink = e.target.closest(Selectors.unenrol);
if (unenrolLink) {
e.preventDefault();
showUnenrolConfirmation(unenrolLink);
}
const showDetailsLink = e.target.closest(Selectors.showDetails);
if (showDetailsLink) {
e.preventDefault();
showStatusDetails(showDetailsLink);
}
});
};
/**
* Show the edit dialogue.
*
* @param {HTMLElement} link
* @param {Function} getBody Function to get the body for the specified user enrolment
*/
const showEditDialogue = (link, getBody) => {
const container = getStatusContainer(link);
const userEnrolmentId = getUserEnrolmentIdFromLink(link);
SaveCancelModal.create({
large: true,
title: Str.get_string('edituserenrolment', 'enrol', container.dataset.fullname),
body: getBody(userEnrolmentId)
})
.then(modal => {
// Handle save event.
modal.getRoot().on(ModalEvents.save, e => {
// Don't close the modal yet.
e.preventDefault();
// Submit form data.
submitEditFormAjax(link, getBody, modal, userEnrolmentId, container.dataset);
});
// Handle hidden event.
modal.getRoot().on(ModalEvents.hidden, () => {
// Destroy when hidden.
modal.destroy();
});
// Show the modal.
modal.show();
return modal;
})
.catch(Notification.exception);
};
/**
* Show and handle the unenrolment confirmation dialogue.
*
* @param {HTMLElement} link
*/
const showUnenrolConfirmation = link => {
const container = getStatusContainer(link);
const userEnrolmentId = getUserEnrolmentIdFromLink(link);
SaveCancelModal.create()
.then(modal => {
// Handle confirm event.
modal.getRoot().on(ModalEvents.save, e => {
// Don't close the modal yet.
e.preventDefault();
// Submit data.
submitUnenrolFormAjax(
link,
modal,
{
ueid: userEnrolmentId,
},
container.dataset
);
});
// Handle hidden event.
modal.getRoot().on(ModalEvents.hidden, () => {
// Destroy when hidden.
modal.destroy();
});
// Display the delete confirmation modal.
modal.show();
const stringData = [
{
key: 'unenrol',
component: 'enrol',
},
{
key: 'unenrolconfirm',
component: 'enrol',
param: {
user: container.dataset.fullname,
course: container.dataset.coursename,
enrolinstancename: container.dataset.enrolinstancename,
}
}
];
return Promise.all([Str.get_strings(stringData), modal]);
})
.then(([strings, modal]) => {
modal.setTitle(strings[0]);
modal.setSaveButtonText(strings[0]);
modal.setBody(strings[1]);
return modal;
})
.catch(Notification.exception);
};
/**
* Show the user details dialogue.
*
* @param {HTMLElement} link
*/
const showStatusDetails = link => {
const container = getStatusContainer(link);
const context = {
editenrollink: '',
statusclass: container.querySelector('span.badge').getAttribute('class'),
...container.dataset,
};
// Find the edit enrolment link.
const editEnrolLink = container.querySelector(Selectors.editEnrolment);
if (editEnrolLink) {
// If there's an edit enrolment link for this user, clone it into the context for the modal.
context.editenrollink = editEnrolLink.outerHTML;
}
CancelModal.create({
large: true,
title: Str.get_string('enroldetails', 'enrol'),
body: Templates.render('core_user/status_details', context),
})
.then(modal => {
if (editEnrolLink) {
modal.getRoot().on('click', Selectors.editEnrolment, e => {
e.preventDefault();
modal.hide();
// Trigger click event for the edit enrolment link to show the edit enrolment modal.
editEnrolLink.click();
});
}
modal.show();
// Handle hidden event.
modal.getRoot().on(ModalEvents.hidden, () => modal.destroy());
return modal;
})
.catch(Notification.exception);
};
/**
* Submit the edit dialogue.
*
* @param {HTMLElement} clickedLink
* @param {Function} getBody
* @param {Object} modal
* @param {Number} userEnrolmentId
* @param {Object} userData
*/
const submitEditFormAjax = (clickedLink, getBody, modal, userEnrolmentId, userData) => {
const form = modal.getRoot().find('form');
Repository.submitUserEnrolmentForm(form.serialize())
.then(data => {
if (!data.result) {
throw data.result;
}
// Dismiss the modal.
modal.hide();
modal.destroy();
return data;
})
.then(() => {
DynamicTable.refreshTableContent(getDynamicTableFromLink(clickedLink))
.catch(Notification.exception);
return Str.get_string('enrolmentupdatedforuser', 'core_enrol', userData);
})
.then(notificationString => {
notifyUser(notificationString);
return;
})
.catch(() => {
modal.setBody(getBody(userEnrolmentId, JSON.stringify(form.serialize())));
return modal;
});
};
/**
* Submit the unenrolment form.
*
* @param {HTMLElement} clickedLink
* @param {Object} modal
* @param {Object} args
* @param {Object} userData
*/
const submitUnenrolFormAjax = (clickedLink, modal, args, userData) => {
Repository.unenrolUser(args.ueid)
.then(data => {
if (!data.result) {
// Display an alert containing the error message
Notification.alert(data.errors[0].key, data.errors[0].message);
return data;
}
// Dismiss the modal.
modal.hide();
modal.destroy();
return data;
})
.then(() => {
DynamicTable.refreshTableContent(getDynamicTableFromLink(clickedLink))
.catch(Notification.exception);
return Str.get_string('unenrolleduser', 'core_enrol', userData);
})
.then(notificationString => {
notifyUser(notificationString);
return;
})
.catch(Notification.exception);
};
/**
* Get the body fragment.
*
* @param {Number} contextId
* @param {Number} ueid The user enrolment id
* @param {Object} formdata
* @returns {Promise}
*/
const getBody = (contextId, ueid, formdata = null) => Fragment.loadFragment(
'enrol',
'user_enrolment_form',
contextId,
{
ueid,
formdata,
}
);
/**
* Initialise the statu field handler.
*
* @param {object} param
* @param {Number} param.contextid
* @param {Number} param.uniqueid
*/
export const init = ({contextid, uniqueid}) => {
registerEventListeners(contextid, uniqueid);
};
+107
View File
@@ -0,0 +1,107 @@
<?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/>.
/**
* Allows you to edit a users profile
*
* @copyright 2015 Shamim Rezaie http://foodle.org
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->dirroot.'/calendar/lib.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/lib.php');
$userid = optional_param('id', $USER->id, PARAM_INT); // User id.
$PAGE->set_url('/user/calendar.php', array('id' => $userid));
list($user, $course) = useredit_setup_preference_page($userid, SITEID);
$defaultlookahead = CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD;
if (isset($CFG->calendar_lookahead)) {
$defaultlookahead = intval($CFG->calendar_lookahead);
}
$defaultmaxevents = CALENDAR_DEFAULT_UPCOMING_MAXEVENTS;
if (isset($CFG->calendar_maxevents)) {
$defaultmaxevents = intval($CFG->calendar_maxevents);
}
// Create form.
$calendarform = new core_user\form\calendar_form(null, array('userid' => $user->id));
$user->timeformat = get_user_preferences('calendar_timeformat', '');
$user->startwday = calendar_get_starting_weekday();
$user->maxevents = get_user_preferences('calendar_maxevents', $defaultmaxevents);
$user->lookahead = get_user_preferences('calendar_lookahead', $defaultlookahead);
$user->persistflt = get_user_preferences('calendar_persistflt', 0);
$calendarform->set_data($user);
$redirect = new moodle_url("/user/preferences.php", array('userid' => $user->id));
if ($calendarform->is_cancelled()) {
redirect($redirect);
} else if ($calendarform->is_submitted() && $calendarform->is_validated() && confirm_sesskey()) {
$data = $calendarform->get_data();
$usernew = ['id' => $USER->id,
'preference_calendar_timeformat' => $data->timeformat,
'preference_calendar_startwday' => $data->startwday,
'preference_calendar_maxevents' => $data->maxevents,
'preference_calendar_lookahead' => $data->lookahead,
'preference_calendar_persistflt' => $data->persistflt
];
useredit_update_user_preference($usernew);
// Calendar type.
$calendartype = $data->calendartype;
// If the specified calendar type does not exist, use the site default.
if (!array_key_exists($calendartype, \core_calendar\type_factory::get_list_of_calendar_types())) {
$calendartype = $CFG->calendartype;
}
$user->calendartype = $calendartype;
// Update user with new calendar type.
user_update_user($user, false, false);
// Trigger event.
\core\event\user_updated::create_from_userid($user->id)->trigger();
if ($USER->id == $user->id) {
$USER->calendartype = $calendartype;
}
redirect($redirect, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
// Display page header.
$streditmycalendar = get_string('calendarpreferences', 'calendar');
$userfullname = fullname($user, true);
$PAGE->navbar->includesettingsbase = true;
$PAGE->set_title("$course->shortname: $streditmycalendar");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($streditmycalendar);
// Finally display THE form.
$calendarform->display();
// And proper footer.
echo $OUTPUT->footer();
@@ -0,0 +1,101 @@
<?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/>.
/**
* User profile set indicator.
*
* @package core_user
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* User profile set indicator.
*
* @package core_user
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_profile_set extends \core_analytics\local\indicator\linear {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:completeduserprofile');
}
/**
* required_sample_data
*
* @return string[]
*/
public static function required_sample_data() {
return array('user');
}
/**
* calculate_sample
*
* @param int $sampleid
* @param string $sampleorigin
* @param int $starttime
* @param int $endtime
* @return float
*/
protected function calculate_sample($sampleid, $sampleorigin, $starttime = false, $endtime = false) {
global $CFG;
$user = $this->retrieve('user', $sampleid);
// Nothing set results in -1.
$calculatedvalue = self::MIN_VALUE;
if (\core_user::awaiting_action($user)) {
return self::MIN_VALUE;
}
if (!$user->confirmed) {
return self::MIN_VALUE;
}
if ($user->description != '') {
$calculatedvalue += 1;
}
if ($user->picture != '') {
$calculatedvalue += 1;
}
// 0.2 for any of the following fields being set (some of them may even be compulsory or have a default).
$fields = array('institution', 'department', 'address', 'city', 'country');
foreach ($fields as $fieldname) {
if ($user->{$fieldname} != '') {
$calculatedvalue += 0.2;
}
}
return $this->limit_value($calculatedvalue);
}
}
@@ -0,0 +1,71 @@
<?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/>.
/**
* User tracks forums indicator.
*
* @package core_user
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\analytics\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* User tracks forums indicator.
*
* @package core_user
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_track_forums extends \core_analytics\local\indicator\binary {
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('indicator:userforumstracking');
}
/**
* required_sample_data
*
* @return string[]
*/
public static function required_sample_data() {
return array('user');
}
/**
* calculate_sample
*
* @param int $sampleid
* @param string $samplesorigin
* @param int $starttime
* @param int $endtime
* @return float
*/
protected function calculate_sample($sampleid, $samplesorigin, $starttime = false, $endtime = false) {
$user = $this->retrieve('user', $sampleid);
return ($user->trackforums) ? self::get_max_value() : self::get_min_value();
}
}
@@ -0,0 +1,274 @@
<?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/>.
/**
* Upcoming activities due target.
*
* @package core
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\analytics\target;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/lib/enrollib.php');
/**
* Upcoming activities due target.
*
* @package core
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class upcoming_activities_due extends \core_analytics\local\target\binary {
/**
* Machine learning backends are not required to predict.
*
* @return bool
*/
public static function based_on_assumptions() {
return true;
}
/**
* Only update last analysis time when analysables are processed.
* @return bool
*/
public function always_update_analysis_time(): bool {
return false;
}
/**
* Only upcoming stuff.
*
* @param \core_analytics\local\time_splitting\base $timesplitting
* @return bool
*/
public function can_use_timesplitting(\core_analytics\local\time_splitting\base $timesplitting): bool {
return ($timesplitting instanceof \core_analytics\local\time_splitting\after_now);
}
/**
* Returns the name.
*
* If there is a corresponding '_help' string this will be shown as well.
*
* @return \lang_string
*/
public static function get_name(): \lang_string {
return new \lang_string('target:upcomingactivitiesdue', 'user');
}
/**
* Overwritten to show a simpler language string.
*
* @param int $modelid
* @param \context $context
* @return string
*/
public function get_insight_subject(int $modelid, \context $context) {
return get_string('youhaveupcomingactivitiesdue');
}
/**
* classes_description
*
* @return string[]
*/
protected static function classes_description() {
return array(
get_string('no'),
get_string('yes'),
);
}
/**
* Returns the predicted classes that will be ignored.
*
* @return array
*/
public function ignored_predicted_classes() {
// No need to process users without upcoming activities due.
return array(0);
}
/**
* get_analyser_class
*
* @return string
*/
public function get_analyser_class() {
return '\core\analytics\analyser\users';
}
/**
* All users are ok.
*
* @param \core_analytics\analysable $analysable
* @param mixed $fortraining
* @return true|string
*/
public function is_valid_analysable(\core_analytics\analysable $analysable, $fortraining = true) {
// The calendar API used by \core_course\analytics\indicator\activities_due is already checking
// if the user has any courses.
return true;
}
/**
* Samples are users and all of them are ok.
*
* @param int $sampleid
* @param \core_analytics\analysable $analysable
* @param bool $fortraining
* @return bool
*/
public function is_valid_sample($sampleid, \core_analytics\analysable $analysable, $fortraining = true) {
return true;
}
/**
* Calculation based on activities due indicator.
*
* @param int $sampleid
* @param \core_analytics\analysable $analysable
* @param int $starttime
* @param int $endtime
* @return float
*/
protected function calculate_sample($sampleid, \core_analytics\analysable $analysable, $starttime = false, $endtime = false) {
$activitiesdueindicator = $this->retrieve('\core_course\analytics\indicator\activities_due', $sampleid);
if ($activitiesdueindicator == \core_course\analytics\indicator\activities_due::get_max_value()) {
return 1;
}
return 0;
}
/**
* No need to link to the insights report in this case.
*
* @return bool
*/
public function link_insights_report(): bool {
return false;
}
/**
* Returns the body message for an insight of a single prediction.
*
* This default method is executed when the analysable used by the model generates one insight
* for each analysable (one_sample_per_analysable === true)
*
* @param \context $context
* @param \stdClass $user
* @param \core_analytics\prediction $prediction
* @param \core_analytics\action[] $actions Passed by reference to remove duplicate links to actions.
* @return array Plain text msg, HTML message and the main URL for this
* insight (you can return null if you are happy with the
* default insight URL calculated in prediction_info())
*/
public function get_insight_body_for_prediction(\context $context, \stdClass $user, \core_analytics\prediction $prediction,
array &$actions) {
global $OUTPUT;
$fullmessageplaintext = get_string('youhaveupcomingactivitiesdueinfo', 'moodle', $user->firstname);
$sampledata = $prediction->get_sample_data();
$activitiesdue = $sampledata['core_course\analytics\indicator\activities_due:extradata'];
if (empty($activitiesdue)) {
// We can throw an exception here because this is a target based on assumptions and we require the
// activities_due indicator.
throw new \coding_exception('The activities_due indicator must be part of the model indicators.');
}
$activitiestext = [];
foreach ($activitiesdue as $key => $activitydue) {
// Human-readable version.
$activitiesdue[$key]->formattedtime = userdate($activitydue->time);
// We provide the URL to the activity through a script that records the user click.
$activityurl = new \moodle_url($activitydue->url);
$actionurl = \core_analytics\prediction_action::transform_to_forward_url($activityurl, 'viewupcoming',
$prediction->get_prediction_data()->id);
$activitiesdue[$key]->url = $actionurl->out(false);
if (count($activitiesdue) === 1) {
// We will use this activity as the main URL of this insight.
$insighturl = $actionurl;
}
$activitiestext[] = $activitydue->name . ': ' . $activitiesdue[$key]->url;
}
foreach ($actions as $key => $action) {
if ($action->get_action_name() === 'viewupcoming') {
// Use it as the main URL of the insight if there are multiple activities due.
if (empty($insighturl)) {
$insighturl = $action->get_url();
}
// Remove the 'viewupcoming' action from the list of actions for this prediction as the action has
// been included in the link to the activity.
unset($actions[$key]);
break;
}
}
$activitieshtml = $OUTPUT->render_from_template('core_user/upcoming_activities_due_insight_body', (object) [
'activitiesdue' => array_values($activitiesdue),
'userfirstname' => $user->firstname
]);
return [
FORMAT_PLAIN => $fullmessageplaintext . PHP_EOL . PHP_EOL . implode(PHP_EOL, $activitiestext) . PHP_EOL,
FORMAT_HTML => $activitieshtml,
'url' => $insighturl,
];
}
/**
* Adds a view upcoming events action.
*
* @param \core_analytics\prediction $prediction
* @param mixed $includedetailsaction
* @param bool $isinsightuser
* @return \core_analytics\prediction_action[]
*/
public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false,
$isinsightuser = false) {
global $CFG, $USER;
$parentactions = parent::prediction_actions($prediction, $includedetailsaction, $isinsightuser);
if (!$isinsightuser && $USER->id != $prediction->get_prediction_data()->sampleid) {
return $parentactions;
}
// We force a lookahead of 30 days so we are sure that the upcoming activities due are shown.
$url = new \moodle_url('/calendar/view.php', ['view' => 'upcoming', 'lookahead' => '30']);
$pix = new \pix_icon('i/calendar', get_string('viewupcomingactivitiesdue', 'calendar'));
$action = new \core_analytics\prediction_action('viewupcoming', $prediction,
$url, $pix, get_string('viewupcomingactivitiesdue', 'calendar'));
return array_merge([$action], $parentactions);
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core_user;
/**
* Update public key against registered user device.
*
* @package core
* @copyright Alex Morris <alex.morris@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class devicekey {
/**
* Update the users public key for the specified device and app.
*
* @param string $uuid The device UUID.
* @param string $appid The app id, usually something like com.moodle.moodlemobile.
* @param string $publickey The app generated public key.
* @return bool
* @since Moodle 4.2
*/
public static function update_device_public_key(string $uuid, string $appid, string $publickey): bool {
global $DB, $USER;
$params = [
'uuid' => $uuid,
'appid' => $appid,
'userid' => $USER->id,
];
if ($DB->record_exists('user_devices', $params)) {
$DB->set_field('user_devices', 'publickey', $publickey, $params);
return true;
}
return false;
}
}
+137
View File
@@ -0,0 +1,137 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
namespace core_user\external;
use core_external\external_api;
use core_external\external_description;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
/**
* Provides the core_user_search_identity external function.
*
* @package core_user
* @category external
* @copyright 2021 David Mudrák <david@moodle.com>
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class search_identity extends external_api {
/**
* Describes the external function parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'query' => new external_value(PARAM_RAW, 'The search query', VALUE_REQUIRED),
]);
}
/**
* Finds users with the identity matching the given query.
*
* @param string $query The search request.
* @return array
*/
public static function execute(string $query): array {
global $DB, $CFG;
$params = external_api::validate_parameters(self::execute_parameters(), [
'query' => $query,
]);
$query = clean_param($params['query'], PARAM_TEXT);
// Validate context.
$context = \context_system::instance();
self::validate_context($context);
require_capability('moodle/user:viewalldetails', $context);
$hasviewfullnames = has_capability('moodle/site:viewfullnames', $context);
$fields = \core_user\fields::for_name()->with_identity($context, false);
$extrafields = $fields->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
list($searchsql, $searchparams) = users_search_sql($query, '', USER_SEARCH_CONTAINS, $extrafields);
list($sortsql, $sortparams) = users_order_by_sql('', $query, $context);
$params = array_merge($searchparams, $sortparams);
$rs = $DB->get_recordset_select('user', $searchsql, $params, $sortsql,
'id' . $fields->get_sql()->selects, 0, $CFG->maxusersperpage + 1);
$count = 0;
$list = [];
foreach ($rs as $record) {
$user = (object)[
'id' => $record->id,
'fullname' => fullname($record, $hasviewfullnames),
'extrafields' => [],
];
foreach ($extrafields as $extrafield) {
// Sanitize the extra fields to prevent potential XSS exploit.
$user->extrafields[] = (object)[
'name' => $extrafield,
'value' => s($record->$extrafield)
];
}
$count++;
if ($count <= $CFG->maxusersperpage) {
$list[$record->id] = $user;
}
}
$rs->close();
return [
'list' => $list,
'maxusersperpage' => $CFG->maxusersperpage,
'overflow' => ($count > $CFG->maxusersperpage),
];
}
/**
* Describes the external function result value.
*
* @return external_description
*/
public static function execute_returns(): external_description {
return new external_single_structure([
'list' => new external_multiple_structure(
new external_single_structure([
'id' => new external_value(\core_user::get_property_type('id'), 'ID of the user'),
// The output of the {@see fullname()} can contain formatting HTML such as <ruby> tags.
// So we need PARAM_RAW here and the caller is supposed to render it appropriately.
'fullname' => new external_value(PARAM_RAW, 'The fullname of the user'),
'extrafields' => new external_multiple_structure(
new external_single_structure([
'name' => new external_value(PARAM_TEXT, 'Name of the extrafield.'),
'value' => new external_value(PARAM_TEXT, 'Value of the extrafield.'),
]), 'List of extra fields', VALUE_OPTIONAL)
])
),
'maxusersperpage' => new external_value(PARAM_INT, 'Configured maximum users per page.'),
'overflow' => new external_value(PARAM_BOOL, 'Were there more records than maxusersperpage found?'),
]);
}
}
+101
View File
@@ -0,0 +1,101 @@
<?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_user\external;
use context_system;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_user\devicekey;
/**
* Update public key against registered user device.
*
* @package core
* @copyright Alex Morris <alex.morris@catalyst.net.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class update_user_device_public_key extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters([
'uuid' => new external_value(PARAM_RAW, 'the device UUID'),
'appid' => new external_value(PARAM_NOTAGS, 'The app id, something like com.moodle.moodlemobile'),
'publickey' => new external_value(PARAM_RAW, 'the app generated public key'),
]);
}
/**
* Update public key against registered user device.
*
* @param string $uuid The device UUID.
* @param string $appid The app id, usually something like com.moodle.moodlemobile.
* @param string $publickey The app generated public key.
* @return array Status and list of possible warnings
*/
public static function execute($uuid, $appid, $publickey): array {
[
'uuid' => $uuid,
'appid' => $appid,
'publickey' => $publickey
] = self::validate_parameters(self::execute_parameters(), [
'uuid' => $uuid,
'appid' => $appid,
'publickey' => $publickey
]);
$context = context_system::instance();
self::validate_context($context);
$warnings = [];
$status = devicekey::update_device_public_key($uuid, $appid, $publickey);
if (!$status) {
$warnings[] = [
'item' => $uuid,
'warningcode' => 'devicedoesnotexist',
'message' => 'Could not find a device with the specified device UUID and app ID for this user'
];
}
return [
'status' => $status,
'warnings' => $warnings,
];
}
/**
* Returns description of method result value.
*
* @return external_single_structure
* @since Moodle 4.2
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'status' => new external_value(PARAM_BOOL, 'Whether the request was successful'),
'warnings' => new external_warnings()
]);
}
}
+145
View File
@@ -0,0 +1,145 @@
<?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 for exporting a user summary from an stdClass.
*
* @package core_user
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\external;
defined('MOODLE_INTERNAL') || die();
use context_system;
use renderer_base;
use moodle_url;
/**
* Class for exporting a user summary from an stdClass.
*
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_summary_exporter extends \core\external\exporter {
protected function get_other_values(renderer_base $output) {
global $PAGE, $CFG;
// Add user picture.
$userpicture = new \user_picture($this->data);
$userpicture->size = 1; // Size f1.
$profileimageurl = $userpicture->get_url($PAGE)->out(false);
$userpicture->size = 0; // Size f2.
$profileimageurlsmall = $userpicture->get_url($PAGE)->out(false);
$profileurl = (new moodle_url('/user/profile.php', array('id' => $this->data->id)))->out(false);
// TODO Does not support custom user profile fields (MDL-70456).
$identityfields = array_flip(\core_user\fields::get_identity_fields(null, false));
$data = $this->data;
foreach ($identityfields as $field => $index) {
if (!empty($data->$field)) {
$identityfields[$field] = $data->$field;
} else {
unset($identityfields[$field]);
}
}
$identity = implode(', ', $identityfields);
return array(
'fullname' => fullname($this->data),
'profileimageurl' => $profileimageurl,
'profileimageurlsmall' => $profileimageurlsmall,
'profileurl' => $profileurl,
'identity' => $identity
);
}
/**
* Get the format parameters for department.
*
* @return array
*/
protected function get_format_parameters_for_department() {
return [
'context' => context_system::instance(), // The system context is cached, so we can get it right away.
];
}
/**
* Get the format parameters for institution.
*
* @return array
*/
protected function get_format_parameters_for_institution() {
return [
'context' => context_system::instance(), // The system context is cached, so we can get it right away.
];
}
public static function define_properties() {
return array(
'id' => array(
'type' => \core_user::get_property_type('id'),
),
'email' => array(
'type' => \core_user::get_property_type('email'),
'default' => ''
),
'idnumber' => array(
'type' => \core_user::get_property_type('idnumber'),
'default' => ''
),
'phone1' => array(
'type' => \core_user::get_property_type('phone1'),
'default' => ''
),
'phone2' => array(
'type' => \core_user::get_property_type('phone2'),
'default' => ''
),
'department' => array(
'type' => \core_user::get_property_type('department'),
'default' => ''
),
'institution' => array(
'type' => \core_user::get_property_type('institution'),
'default' => ''
)
);
}
public static function define_other_properties() {
return array(
'fullname' => array(
'type' => PARAM_RAW
),
'identity' => array(
'type' => PARAM_RAW
),
'profileurl' => array(
'type' => PARAM_URL
),
'profileimageurl' => array(
'type' => PARAM_URL
),
'profileimageurlsmall' => array(
'type' => PARAM_URL
),
);
}
}
+685
View File
@@ -0,0 +1,685 @@
<?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_user;
use core_text;
/**
* Class for retrieving information about user fields that are needed for displaying user identity.
*
* @package core_user
*/
class fields {
/** @var string Prefix used to identify custom profile fields */
const PROFILE_FIELD_PREFIX = 'profile_field_';
/** @var string Regular expression used to match a field name against the prefix */
const PROFILE_FIELD_REGEX = '~^' . self::PROFILE_FIELD_PREFIX . '(.*)$~';
/** @var int All fields required to display user's identity, based on server configuration */
const PURPOSE_IDENTITY = 0;
/** @var int All fields required to display a user picture */
const PURPOSE_USERPIC = 1;
/** @var int All fields required for somebody's name */
const PURPOSE_NAME = 2;
/** @var int Field required by custom include list */
const CUSTOM_INCLUDE = 3;
/** @var \context|null Context in use */
protected $context;
/** @var bool True to allow custom user fields */
protected $allowcustom;
/** @var bool[] Array of purposes (from PURPOSE_xx to true/false) */
protected $purposes;
/** @var string[] List of extra fields to include */
protected $include;
/** @var string[] List of fields to exclude */
protected $exclude;
/** @var int Unique identifier for different queries generated in same request */
protected static $uniqueidentifier = 1;
/** @var array|null Associative array from field => array of purposes it was used for => true */
protected $fields = null;
/**
* Protected constructor - use one of the for_xx methods to create an object.
*
* @param int $purpose Initial purpose for object or -1 for none
*/
protected function __construct(int $purpose = -1) {
$this->purposes = [
self::PURPOSE_IDENTITY => false,
self::PURPOSE_USERPIC => false,
self::PURPOSE_NAME => false,
];
if ($purpose != -1) {
$this->purposes[$purpose] = true;
}
$this->include = [];
$this->exclude = [];
$this->context = null;
$this->allowcustom = true;
}
/**
* Constructs an empty user fields object to get arbitrary user fields.
*
* You can add fields to retrieve with the including() function.
*
* @return fields User fields object ready for use
*/
public static function empty(): fields {
return new fields();
}
/**
* Constructs a user fields object to get identity information for display.
*
* The function does all the required capability checks to see if the current user is allowed
* to see them in the specified context. You can pass context null to get all the fields without
* checking permissions.
*
* If the code can only handle fields in the main user table, and not custom profile fields,
* then set $allowcustom to false.
*
* Note: After constructing the object you can use the ->with_xx, ->including, and ->excluding
* functions to control the required fields in more detail. For example:
*
* $fields = fields::for_identity($context)->with_userpic()->excluding('email');
*
* @param \context|null $context Context; if supplied, includes only fields the current user should see
* @param bool $allowcustom If true, custom profile fields may be included
* @return fields User fields object ready for use
*/
public static function for_identity(?\context $context, bool $allowcustom = true): fields {
$fields = new fields(self::PURPOSE_IDENTITY);
$fields->context = $context;
$fields->allowcustom = $allowcustom;
return $fields;
}
/**
* Constructs a user fields object to get information required for displaying a user picture.
*
* Note: After constructing the object you can use the ->with_xx, ->including, and ->excluding
* functions to control the required fields in more detail. For example:
*
* $fields = fields::for_userpic()->with_name()->excluding('email');
*
* @return fields User fields object ready for use
*/
public static function for_userpic(): fields {
return new fields(self::PURPOSE_USERPIC);
}
/**
* Constructs a user fields object to get information required for displaying a user full name.
*
* Note: After constructing the object you can use the ->with_xx, ->including, and ->excluding
* functions to control the required fields in more detail. For example:
*
* $fields = fields::for_name()->with_userpic()->excluding('email');
*
* @return fields User fields object ready for use
*/
public static function for_name(): fields {
return new fields(self::PURPOSE_NAME);
}
/**
* On an existing fields object, adds the fields required for displaying user pictures.
*
* @return $this Same object for chaining function calls
*/
public function with_userpic(): fields {
$this->purposes[self::PURPOSE_USERPIC] = true;
return $this;
}
/**
* On an existing fields object, adds the fields required for displaying user full names.
*
* @return $this Same object for chaining function calls
*/
public function with_name(): fields {
$this->purposes[self::PURPOSE_NAME] = true;
return $this;
}
/**
* On an existing fields object, adds the fields required for displaying user identity.
*
* The function does all the required capability checks to see if the current user is allowed
* to see them in the specified context. You can pass context null to get all the fields without
* checking permissions.
*
* If the code can only handle fields in the main user table, and not custom profile fields,
* then set $allowcustom to false.
*
* @param \context|null Context; if supplied, includes only fields the current user should see
* @param bool $allowcustom If true, custom profile fields may be included
* @return $this Same object for chaining function calls
*/
public function with_identity(?\context $context, bool $allowcustom = true): fields {
$this->context = $context;
$this->allowcustom = $allowcustom;
$this->purposes[self::PURPOSE_IDENTITY] = true;
return $this;
}
/**
* On an existing fields object, adds extra fields to be retrieved. You can specify either
* fields from the user table e.g. 'email', or profile fields e.g. 'profile_field_height'.
*
* @param string ...$include One or more fields to add
* @return $this Same object for chaining function calls
*/
public function including(string ...$include): fields {
$this->include = array_merge($this->include, $include);
return $this;
}
/**
* On an existing fields object, excludes fields from retrieval. You can specify either
* fields from the user table e.g. 'email', or profile fields e.g. 'profile_field_height'.
*
* This is useful when constructing queries where your query already explicitly references
* certain fields, so you don't want to retrieve them twice.
*
* @param string ...$exclude One or more fields to exclude
* @return $this Same object for chaining function calls
*/
public function excluding(...$exclude): fields {
$this->exclude = array_merge($this->exclude, $exclude);
return $this;
}
/**
* Gets an array of all fields that are required for the specified purposes, also taking
* into account the $includes and $excludes settings.
*
* The results may include basic field names (columns from the 'user' database table) and,
* unless turned off, custom profile field names in the format 'profile_field_myfield'.
*
* You should not rely on the order of fields, with one exception: if there is an id field
* it will be returned first. This is in case it is used with get_records calls.
*
* The $limitpurposes parameter is useful if you want to get a different set of fields than the
* purposes in the constructor. For example, if you want to get SQL for identity + user picture
* fields, but you then want to only get the identity fields as a list. (You can only specify
* purposes that were also passed to the constructor i.e. it can only be used to restrict the
* list, not add to it.)
*
* @param array $limitpurposes If specified, gets fields only for these purposes
* @return string[] Array of required fields
* @throws \coding_exception If any unknown purpose is listed
*/
public function get_required_fields(array $limitpurposes = []): array {
// The first time this is called, actually work out the list. There is no way to 'un-cache'
// it, but these objects are designed to be short-lived so it doesn't need one.
if ($this->fields === null) {
// Add all the fields as array keys so that there are no duplicates.
$this->fields = [];
if ($this->purposes[self::PURPOSE_IDENTITY]) {
foreach (self::get_identity_fields($this->context, $this->allowcustom) as $field) {
$this->fields[$field] = [self::PURPOSE_IDENTITY => true];
}
}
if ($this->purposes[self::PURPOSE_USERPIC]) {
foreach (self::get_picture_fields() as $field) {
if (!array_key_exists($field, $this->fields)) {
$this->fields[$field] = [];
}
$this->fields[$field][self::PURPOSE_USERPIC] = true;
}
}
if ($this->purposes[self::PURPOSE_NAME]) {
foreach (self::get_name_fields() as $field) {
if (!array_key_exists($field, $this->fields)) {
$this->fields[$field] = [];
}
$this->fields[$field][self::PURPOSE_NAME] = true;
}
}
foreach ($this->include as $field) {
if ($this->allowcustom || !preg_match(self::PROFILE_FIELD_REGEX, $field)) {
if (!array_key_exists($field, $this->fields)) {
$this->fields[$field] = [];
}
$this->fields[$field][self::CUSTOM_INCLUDE] = true;
}
}
foreach ($this->exclude as $field) {
unset($this->fields[$field]);
}
// If the id field is included, make sure it's first in the list.
if (array_key_exists('id', $this->fields)) {
$newfields = ['id' => $this->fields['id']];
foreach ($this->fields as $field => $purposes) {
if ($field !== 'id') {
$newfields[$field] = $purposes;
}
}
$this->fields = $newfields;
}
}
if ($limitpurposes) {
// Check the value was legitimate.
foreach ($limitpurposes as $purpose) {
if ($purpose != self::CUSTOM_INCLUDE && empty($this->purposes[$purpose])) {
throw new \coding_exception('$limitpurposes can only include purposes defined in object');
}
}
// Filter the fields to include only those matching the purposes.
$result = [];
foreach ($this->fields as $key => $purposes) {
foreach ($limitpurposes as $purpose) {
if (array_key_exists($purpose, $purposes)) {
$result[] = $key;
break;
}
}
}
return $result;
} else {
return array_keys($this->fields);
}
}
/**
* Gets fields required for user pictures.
*
* The results include only basic field names (columns from the 'user' database table).
*
* @return string[] All fields required for user pictures
*/
public static function get_picture_fields(): array {
return ['id', 'picture', 'firstname', 'lastname', 'firstnamephonetic', 'lastnamephonetic',
'middlename', 'alternatename', 'imagealt', 'email'];
}
/**
* Gets fields required for user names.
*
* The results include only basic field names (columns from the 'user' database table).
*
* Fields are usually returned in a specific order, which the fullname() function depends on.
* If you specify 'true' to the $strangeorder flag, then the firstname and lastname fields
* are moved to the front; this is useful in a few places in existing code. New code should
* avoid requiring a particular order.
*
* @param bool $differentorder In a few places, a different order of fields is required
* @return string[] All fields used to display user names
*/
public static function get_name_fields(bool $differentorder = false): array {
$fields = ['firstnamephonetic', 'lastnamephonetic', 'middlename', 'alternatename',
'firstname', 'lastname'];
if ($differentorder) {
return array_merge(array_slice($fields, -2), array_slice($fields, 0, -2));
} else {
return $fields;
}
}
/**
* Gets all fields required for user identity. These fields should be included in tables
* showing lists of users (in addition to the user's name which is included as standard).
*
* The results include basic field names (columns from the 'user' database table) and, unless
* turned off, custom profile field names in the format 'profile_field_myfield', note these
* fields will always be returned lower cased to match how they are returned by the DML library.
*
* This function does all the required capability checks to see if the current user is allowed
* to see them in the specified context. You can pass context null to get all the fields
* without checking permissions.
*
* @param \context|null $context Context; if not supplied, all fields will be included without checks
* @param bool $allowcustom If true, custom profile fields will be included
* @return string[] Array of required fields
* @throws \coding_exception
*/
public static function get_identity_fields(?\context $context, bool $allowcustom = true): array {
global $CFG;
// Only users with permission get the extra fields.
if ($context && !has_capability('moodle/site:viewuseridentity', $context)) {
return [];
}
// Split showuseridentity on comma (filter needed in case the showuseridentity is empty).
$extra = array_filter(explode(',', $CFG->showuseridentity));
// If there are any custom fields, remove them if necessary (either if allowcustom is false,
// or if the user doesn't have access to see them).
foreach ($extra as $key => $field) {
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
$allowed = false;
if ($allowcustom) {
require_once($CFG->dirroot . '/user/profile/lib.php');
// Ensure the field exists (it may have been deleted since user identity was configured).
$field = profile_get_custom_field_data_by_shortname($matches[1], false);
if ($field !== null) {
$fieldinstance = profile_get_user_field($field->datatype, $field->id, 0, $field);
$allowed = $fieldinstance->is_visible($context);
}
}
if (!$allowed) {
unset($extra[$key]);
}
}
}
// For standard user fields, access is controlled by the hiddenuserfields option and
// some different capabilities. Check and remove these if the user can't access them.
$hiddenfields = array_filter(explode(',', $CFG->hiddenuserfields));
$hiddenidentifiers = array_intersect($extra, $hiddenfields);
if ($hiddenidentifiers) {
if (!$context) {
$canviewhiddenuserfields = true;
} else if ($context->get_course_context(false)) {
// We are somewhere inside a course.
$canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
} else {
// We are not inside a course.
$canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
}
if (!$canviewhiddenuserfields) {
// Remove hidden identifiers from the list.
$extra = array_diff($extra, $hiddenidentifiers);
}
}
// Re-index the entries and return.
$extra = array_values($extra);
return array_map([core_text::class, 'strtolower'], $extra);
}
/**
* Gets SQL that can be used in a query to get the necessary fields.
*
* The result of this function is an object with fields 'selects', 'joins', 'params', and
* 'mappings'.
*
* If not empty, the list of selects will begin with a comma and the list of joins will begin
* and end with a space. You can include the result in your existing query like this:
*
* SELECT (your existing fields)
* $selects
* FROM {user} u
* JOIN (your existing joins)
* $joins
*
* When there are no custom fields then the 'joins' result will always be an empty string, and
* 'params' will be an empty array.
*
* The $fieldmappings value is often not needed. It is an associative array from each field
* name to an SQL expression for the value of that field, e.g.:
* 'profile_field_frog' => 'uf1d_3.data'
* 'city' => 'u.city'
* This is helpful if you want to use the profile fields in a WHERE clause, becuase you can't
* refer to the aliases used in the SELECT list there.
*
* The leading comma is included because this makes it work in the pattern above even if there
* are no fields from the get_sql() data (which can happen if doing identity fields and none
* are selected). If you want the result without a leading comma, set $leadingcomma to false.
*
* If the 'id' field is included then it will always be first in the list. Otherwise, you
* should not rely on the field order.
*
* For identity fields, the function does all the required capability checks to see if the
* current user is allowed to see them in the specified context. You can pass context null
* to get all the fields without checking permissions.
*
* If your code for any reason cannot cope with custom fields then you can turn them off.
*
* You can have either named or ? params. If you use named params, they are of the form
* uf1s_2; the first number increments in each call using a static variable in this class and
* the second number refers to the field being queried. A similar pattern is used to make
* join aliases unique.
*
* If your query refers to the user table by an alias e.g. 'u' then specify this in the $alias
* parameter; otherwise it will use {user} (if there are any joins for custom profile fields)
* or simply refer to the field by name only (if there aren't).
*
* If you need to use a prefix on the field names (for example in case they might coincide with
* existing result columns from your query, or if you want a convenient way to split out all
* the user data into a separate object) then you can specify one here. For example, if you
* include name fields and the prefix is 'u_' then the results will include 'u_firstname'.
*
* If you don't want to prefix all the field names but only change the id field name, use
* the $renameid parameter. (When you use this parameter, it takes precedence over any prefix;
* the id field will not be prefixed, while all others will.)
*
* @param string $alias Optional (but recommended) alias for user table in query, e.g. 'u'
* @param bool $namedparams If true, uses named :parameters instead of indexed ? parameters
* @param string $prefix Optional prefix for all field names in result, e.g. 'u_'
* @param string $renameid Renames the 'id' field if specified, e.g. 'userid'
* @param bool $leadingcomma If true the 'selects' list will start with a comma
* @return \stdClass Object with necessary SQL components
*/
public function get_sql(string $alias = '', bool $namedparams = false, string $prefix = '',
string $renameid = '', bool $leadingcomma = true): \stdClass {
global $DB;
$fields = $this->get_required_fields();
$selects = '';
$joins = '';
$params = [];
$mappings = [];
$unique = self::$uniqueidentifier++;
$fieldcount = 0;
if ($alias) {
$usertable = $alias . '.';
} else {
// If there is no alias, we still need to use {user} to identify the table when there
// are joins with other tables. When there are no customfields then there are no joins
// so we can refer to the fields by name alone.
$gotcustomfields = false;
foreach ($fields as $field) {
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
$gotcustomfields = true;
break;
}
}
if ($gotcustomfields) {
$usertable = '{user}.';
} else {
$usertable = '';
}
}
foreach ($fields as $field) {
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
// Custom profile field.
$shortname = $matches[1];
$fieldcount++;
$fieldalias = 'uf' . $unique . 'f_' . $fieldcount;
$dataalias = 'uf' . $unique . 'd_' . $fieldcount;
if ($namedparams) {
$withoutcolon = 'uf' . $unique . 's' . $fieldcount;
$placeholder = ':' . $withoutcolon;
$params[$withoutcolon] = $shortname;
} else {
$placeholder = '?';
$params[] = $shortname;
}
$joins .= " JOIN {user_info_field} $fieldalias ON " .
$DB->sql_equal($fieldalias . '.shortname', $placeholder, false) . "
LEFT JOIN {user_info_data} $dataalias ON $dataalias.fieldid = $fieldalias.id
AND $dataalias.userid = {$usertable}id";
// For Oracle we need to convert the field into a usable format.
$fieldsql = $DB->sql_compare_text($dataalias . '.data', 255);
$selects .= ", $fieldsql AS $prefix$field";
$mappings[$field] = $fieldsql;
} else {
// Standard user table field.
$selects .= ", $usertable$field";
if ($field === 'id' && $renameid && $renameid !== 'id') {
$selects .= " AS $renameid";
} else if ($prefix) {
$selects .= " AS $prefix$field";
}
$mappings[$field] = "$usertable$field";
}
}
// Add a space to the end of the joins list; this means it can be appended directly into
// any existing query without worrying about whether the developer has remembered to add
// whitespace after it.
if ($joins) {
$joins .= ' ';
}
// Optionally remove the leading comma.
if (!$leadingcomma) {
$selects = ltrim($selects, ' ,');
}
return (object)['selects' => $selects, 'joins' => $joins, 'params' => $params,
'mappings' => $mappings];
}
/**
* Similar to {@see \moodle_database::sql_fullname} except it returns all user name fields as defined by site config, in a
* single select statement suitable for inclusion in a query/filter for a users fullname, e.g.
*
* [$select, $params] = fields::get_sql_fullname('u');
* $users = $DB->get_records_sql_menu("SELECT u.id, {$select} FROM {user} u", $params);
*
* @param string|null $tablealias User table alias, if set elsewhere in the query, null if not required
* @param bool $override If true then the alternativefullnameformat format rather than fullnamedisplay format will be used
* @return array SQL select snippet and parameters
*/
public static function get_sql_fullname(?string $tablealias = 'u', bool $override = false): array {
global $DB;
$unique = self::$uniqueidentifier++;
$namefields = self::get_name_fields();
// Create a dummy user object containing all name fields.
$dummyuser = (object) array_combine($namefields, $namefields);
$dummyfullname = fullname($dummyuser, $override);
// Extract any name fields from the fullname format in the order that they appear.
$matchednames = array_values(order_in_string($namefields, $dummyfullname));
$namelookup = $namepattern = $elements = $params = [];
foreach ($namefields as $index => $namefield) {
$namefieldwithalias = $tablealias ? "{$tablealias}.{$namefield}" : $namefield;
// Coalesce the name fields to ensure we don't return null.
$emptyparam = "uf{$unique}ep_{$index}";
$namelookup[$namefield] = "COALESCE({$namefieldwithalias}, :{$emptyparam})";
$params[$emptyparam] = '';
$namepattern[] = '\b' . preg_quote($namefield) . '\b';
}
// Grab any content between the name fields, inserting them after each name field.
$chunks = preg_split('/(' . implode('|', $namepattern) . ')/', $dummyfullname);
foreach ($chunks as $index => $chunk) {
if ($index > 0) {
$elements[] = $namelookup[$matchednames[$index - 1]];
}
if (core_text::strlen($chunk) > 0) {
// If content is just whitespace, add to elements directly (also Oracle doesn't support passing ' ' as param).
if (preg_match('/^\s+$/', $chunk)) {
$elements[] = "'$chunk'";
} else {
$elementparam = "uf{$unique}fp_{$index}";
$elements[] = ":{$elementparam}";
$params[$elementparam] = $chunk;
}
}
}
return [$DB->sql_concat(...$elements), $params];
}
/**
* Gets the display name of a given user field.
*
* Supports field names from the 'user' database table, and custom profile fields supplied in
* the format 'profile_field_xx'.
*
* @param string $field Field name in database
* @return string Field name for display to user
* @throws \coding_exception
*/
public static function get_display_name(string $field): string {
global $CFG;
// Custom fields have special handling.
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
require_once($CFG->dirroot . '/user/profile/lib.php');
$fieldinfo = profile_get_custom_field_data_by_shortname($matches[1], false);
// Use format_string so it can be translated with multilang filter if necessary.
return $fieldinfo ? format_string($fieldinfo->name) : $field;
}
// Some fields have language strings which are not the same as field name.
switch ($field) {
case 'picture' : {
return get_string('pictureofuser');
}
}
// Otherwise just use the same lang string.
return get_string($field);
}
/**
* Resets the unique identifier used to ensure that multiple SQL fragments generated in the
* same request will have different identifiers for parameters and table aliases.
*
* This is intended only for use in unit testing.
*/
public static function reset_unique_identifier() {
self::$uniqueidentifier = 1;
}
/**
* Checks if a field name looks like a custom profile field i.e. it begins with profile_field_
* (does not check if that profile field actually exists).
*
* @param string $fieldname Field name
* @return string Empty string if not a profile field, or profile field name (without profile_field_)
*/
public static function match_custom_field(string $fieldname): string {
if (preg_match(self::PROFILE_FIELD_REGEX, $fieldname, $matches)) {
return $matches[1];
} else {
return '';
}
}
}
+157
View File
@@ -0,0 +1,157 @@
<?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/>.
/**
* Form to edit a users preferred language
*
* @copyright 2015 Shamim Rezaie http://foodle.org
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
namespace core_user\form;
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Class user_edit_calendar_form.
*
* @copyright 2015 Shamim Rezaie http://foodle.org
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class calendar_form extends \moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG, $USER;
$mform = $this->_form;
$userid = $USER->id;
if (is_array($this->_customdata)) {
if (array_key_exists('userid', $this->_customdata)) {
$userid = $this->_customdata['userid'];
}
}
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
// We do not want to show this option unless there is more than one calendar type to display.
if (count(\core_calendar\type_factory::get_list_of_calendar_types()) > 1) {
$calendartypes = \core_calendar\type_factory::get_list_of_calendar_types();
$mform->addElement('select', 'calendartype', get_string('preferredcalendar', 'calendar'), $calendartypes);
$mform->setType('calendartype', PARAM_ALPHANUM);
$mform->setDefault('calendartype', $CFG->calendartype);
} else {
$mform->addElement('hidden', 'calendartype', $CFG->calendartype);
$mform->setType('calendartype', PARAM_ALPHANUM);
}
// Date / Time settings.
$options = array(
'0' => get_string('default', 'calendar'),
CALENDAR_TF_12 => get_string('timeformat_12', 'calendar'),
CALENDAR_TF_24 => get_string('timeformat_24', 'calendar')
);
$mform->addElement('select', 'timeformat', get_string('pref_timeformat', 'calendar'), $options);
$mform->addHelpButton('timeformat', 'pref_timeformat', 'calendar');
// First day of week.
$options = array(
0 => get_string('sunday', 'calendar'),
1 => get_string('monday', 'calendar'),
2 => get_string('tuesday', 'calendar'),
3 => get_string('wednesday', 'calendar'),
4 => get_string('thursday', 'calendar'),
5 => get_string('friday', 'calendar'),
6 => get_string('saturday', 'calendar')
);
$mform->addElement('select', 'startwday', get_string('pref_startwday', 'calendar'), $options);
$mform->addHelpButton('startwday', 'pref_startwday', 'calendar');
// Maximum events to display.
$options = array();
for ($i = 1; $i <= 20; $i++) {
$options[$i] = $i;
}
$mform->addElement('select', 'maxevents', get_string('pref_maxevents', 'calendar'), $options);
$mform->addHelpButton('maxevents', 'pref_maxevents', 'calendar');
// Calendar lookahead.
$options = array(365 => new \lang_string('numyear', '', 1),
270 => get_string('nummonths', '', 9),
180 => get_string('nummonths', '', 6),
150 => get_string('nummonths', '', 5),
120 => get_string('nummonths', '', 4),
90 => get_string('nummonths', '', 3),
60 => get_string('nummonths', '', 2),
30 => get_string('nummonth', '', 1),
21 => get_string('numweeks', '', 3),
14 => get_string('numweeks', '', 2),
7 => get_string('numweek', '', 1),
6 => get_string('numdays', '', 6),
5 => get_string('numdays', '', 5),
4 => get_string('numdays', '', 4),
3 => get_string('numdays', '', 3),
2 => get_string('numdays', '', 2),
1 => get_string('numday', '', 1));
$mform->addElement('select', 'lookahead', get_string('pref_lookahead', 'calendar'), $options);
$mform->addHelpButton('lookahead', 'pref_lookahead', 'calendar');
// Remember event filtering.
$options = array(
0 => get_string('no'),
1 => get_string('yes')
);
$mform->addElement('select', 'persistflt', get_string('pref_persistflt', 'calendar'), $options);
$mform->addHelpButton('persistflt', 'pref_persistflt', 'calendar');
$this->add_action_buttons(true, get_string('savechanges'));
}
/**
* Extend the form definition after the data has been parsed.
*/
public function definition_after_data() {
global $CFG;
$mform = $this->_form;
// If calendar type does not exist, use site default calendar type.
if ($calendarselected = $mform->getElementValue('calendartype')) {
if (is_array($calendarselected)) {
// There are multiple calendar types available.
$calendar = reset($calendarselected);
} else {
// There is only one calendar type available.
$calendar = $calendarselected;
}
// Check calendar type exists.
if (!array_key_exists($calendar, \core_calendar\type_factory::get_list_of_calendar_types())) {
$calendartypeel = $mform->getElement('calendartype');
$calendartypeel->setValue($CFG->calendartype);
}
}
}
}
@@ -0,0 +1,109 @@
<?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_user\form;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Contact site support form.
*
* @package core_user
* @copyright 2022 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class contactsitesupport_form extends \moodleform {
/**
* Define the contact site support form.
*/
public function definition(): void {
global $CFG;
$mform = $this->_form;
$user = $this->_customdata;
$strrequired = get_string('required');
// Name.
$mform->addElement('text', 'name', get_string('name'));
$mform->addRule('name', $strrequired, 'required', null, 'client');
$mform->setType('name', PARAM_TEXT);
// Email.
$mform->addElement('text', 'email', get_string('email'));
$mform->addRule('email', get_string('missingemail'), 'required', null, 'client');
$mform->setType('email', PARAM_EMAIL);
// Subject.
$mform->addElement('text', 'subject', get_string('subject'));
$mform->addRule('subject', $strrequired, 'required', null, 'client');
$mform->setType('subject', PARAM_TEXT);
// Message.
$mform->addElement('textarea', 'message', get_string('message'));
$mform->addRule('message', $strrequired, 'required', null, 'client');
$mform->setType('message', PARAM_TEXT);
// If the user is logged in set name and email fields to the current user info.
if (isloggedin() && !isguestuser()) {
$mform->setDefault('name', fullname($user));
$mform->hardFreeze('name');
$mform->setDefault('email', $user->email);
$mform->hardFreeze('email');
}
if (!empty($CFG->recaptchapublickey) && !empty($CFG->recaptchaprivatekey)) {
$mform->addElement('recaptcha', 'recaptcha_element', get_string('security_question', 'auth'));
$mform->addHelpButton('recaptcha_element', 'recaptcha', 'auth');
$mform->closeHeaderBefore('recaptcha_element');
}
$this->add_action_buttons(true, get_string('submit'));
}
/**
* Validate user supplied data on the contact site support form.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
public function validation($data, $files): array {
$errors = parent::validation($data, $files);
if (!validate_email($data['email'])) {
$errors['email'] = get_string('invalidemail');
}
if ($this->_form->elementExists('recaptcha_element')) {
$recaptchaelement = $this->_form->getElement('recaptcha_element');
if (!empty($this->_form->_submitValues['g-recaptcha-response'])) {
$response = $this->_form->_submitValues['g-recaptcha-response'];
if (!$recaptchaelement->verify($response)) {
$errors['recaptcha_element'] = get_string('incorrectpleasetryagain', 'auth');
}
} else {
$errors['recaptcha_element'] = get_string('missingrecaptchachallengefield');
}
}
return $errors;
}
}
@@ -0,0 +1,53 @@
<?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_user\form;
use \core_contentbank\content;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Form to edit a user's preferences concerning the content bank.
*
* @package core_user
* @copyright 2020 François Moreau
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class contentbank_user_preferences_form extends \moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG, $USER;
$mform = $this->_form;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$options = [
content::VISIBILITY_PUBLIC => get_string('visibilitychoicepublic', 'core_contentbank'),
content::VISIBILITY_UNLISTED => get_string('visibilitychoiceunlisted', 'core_contentbank')
];
$mform->addElement('select', 'contentvisibility', get_string('visibilitypref', 'core_contentbank'), $options);
$mform->addHelpButton('contentvisibility', 'visibilitypref', 'core_contentbank');
$this->add_action_buttons(true, get_string('savechanges'));
}
}
@@ -0,0 +1,64 @@
<?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/>.
/**
* Form to allow user to set their default home page
*
* @package core_user
* @copyright 2019 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\form;
use lang_string;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot . '/lib/formslib.php');
/**
* Form class
*
* @copyright 2019 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class defaulthomepage_form extends \moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG;
$mform = $this->_form;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$options = [HOMEPAGE_SITE => new lang_string('home')];
if (!empty($CFG->enabledashboard)) {
$options[HOMEPAGE_MY] = new lang_string('mymoodle', 'admin');
}
$options[HOMEPAGE_MYCOURSES] = new lang_string('mycourses', 'admin');
$mform->addElement('select', 'defaulthomepage', get_string('defaulthomepageuser'), $options);
$mform->addHelpButton('defaulthomepage', 'defaulthomepageuser');
$mform->setDefault('defaulthomepage', get_default_home_page());
$this->add_action_buttons(true, get_string('savechanges'));
}
}
+192
View File
@@ -0,0 +1,192 @@
<?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_user\form;
use html_writer;
use moodle_url;
/**
* Manage user private area files form
*
* @package core_user
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class private_files extends \core_form\dynamic_form {
/**
* Add elements to this form.
*/
public function definition() {
global $OUTPUT;
$mform = $this->_form;
$options = $this->get_options();
// Show file area space usage.
$maxareabytes = $options['areamaxbytes'];
if ($maxareabytes != FILE_AREA_MAX_BYTES_UNLIMITED) {
$fileareainfo = file_get_file_area_info($this->get_context_for_dynamic_submission()->id, 'user', 'private');
// Display message only if we have files.
if ($fileareainfo['filecount']) {
$a = (object) [
'used' => display_size($fileareainfo['filesize_without_references']),
'total' => display_size($maxareabytes, 0)
];
$quotamsg = get_string('quotausage', 'moodle', $a);
$notification = new \core\output\notification($quotamsg, \core\output\notification::NOTIFY_INFO);
$mform->addElement('static', 'areabytes', '', $OUTPUT->render($notification));
}
}
$mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options);
if ($link = $this->get_emaillink()) {
$emaillink = html_writer::link(new moodle_url('mailto:' . $link), $link);
$mform->addElement('static', 'emailaddress', '',
get_string('emailtoprivatefiles', 'moodle', $emaillink));
}
$mform->setType('returnurl', PARAM_LOCALURL);
// The 'nosubmit' param (default false) determines whether we should show the standard form action buttons (save/cancel).
// This value is set when the form is displayed within a modal, which adds the action buttons itself.
if (!$this->optional_param('nosubmit', false, PARAM_BOOL)) {
$this->add_action_buttons();
}
}
/**
* Validate incoming data.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
$errors = array();
$draftitemid = $data['files_filemanager'];
$options = $this->get_options();
if (file_is_draft_area_limit_reached($draftitemid, $options['areamaxbytes'])) {
$errors['files_filemanager'] = get_string('userquotalimit', 'error');
}
return $errors;
}
/**
* Link to email private files
*
* @return string|null
* @throws \coding_exception
*/
protected function get_emaillink() {
global $USER;
// Attempt to generate an inbound message address to support e-mail to private files.
$generator = new \core\message\inbound\address_manager();
$generator->set_handler('\core\message\inbound\private_files_handler');
$generator->set_data(-1);
return $generator->generate($USER->id);
}
/**
* Check if current user has access to this form, otherwise throw exception
*
* Sometimes permission check may depend on the action and/or id of the entity.
* If necessary, form data is available in $this->_ajaxformdata or
* by calling $this->optional_param()
*/
protected function check_access_for_dynamic_submission(): void {
require_capability('moodle/user:manageownfiles', $this->get_context_for_dynamic_submission());
}
/**
* Returns form context
*
* If context depends on the form data, it is available in $this->_ajaxformdata or
* by calling $this->optional_param()
*
* @return \context
*/
protected function get_context_for_dynamic_submission(): \context {
global $USER;
return \context_user::instance($USER->id);
}
/**
* File upload options
*
* @return array
* @throws \coding_exception
*/
protected function get_options(): array {
global $CFG;
$maxbytes = $CFG->userquota;
$maxareabytes = $CFG->userquota;
if (has_capability('moodle/user:ignoreuserquota', $this->get_context_for_dynamic_submission())) {
$maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS;
$maxareabytes = FILE_AREA_MAX_BYTES_UNLIMITED;
}
return ['subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'accepted_types' => '*',
'areamaxbytes' => $maxareabytes];
}
/**
* Process the form submission, used if form was submitted via AJAX
*
* This method can return scalar values or arrays that can be json-encoded, they will be passed to the caller JS.
*
* Submission data can be accessed as: $this->get_data()
*
* @return mixed
*/
public function process_dynamic_submission() {
file_postupdate_standard_filemanager($this->get_data(), 'files',
$this->get_options(), $this->get_context_for_dynamic_submission(), 'user', 'private', 0);
return null;
}
/**
* Load in existing data as form defaults
*
* Can be overridden to retrieve existing values from db by entity id and also
* to preprocess editor and filemanager elements
*
* Example:
* $this->set_data(get_entity($this->_ajaxformdata['id']));
*/
public function set_data_for_dynamic_submission(): void {
$data = new \stdClass();
file_prepare_standard_filemanager($data, 'files', $this->get_options(),
$this->get_context_for_dynamic_submission(), 'user', 'private', 0);
$this->set_data($data);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* This is used in the form elements sensitive to the page url, such as Atto autosave in 'editor'
*
* If the form has arguments (such as 'id' of the element being edited), the URL should
* also have respective argument.
*
* @return \moodle_url
*/
protected function get_page_url_for_dynamic_submission(): \moodle_url {
return new moodle_url('/user/files.php');
}
}
+125
View File
@@ -0,0 +1,125 @@
<?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_user\form;
use context;
use core_form\dynamic_form;
use moodle_url;
/**
* Modal form to edit profile category
*
* @package core_user
* @copyright 2021 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class profile_category_form extends dynamic_form {
/**
* Form definition
*/
protected function definition() {
$mform = $this->_form;
$strrequired = get_string('required');
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'editcategory');
$mform->setType('action', PARAM_ALPHANUMEXT);
$mform->addElement('text', 'name', get_string('profilecategoryname', 'admin'), 'maxlength="255" size="30"');
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', $strrequired, 'required', null, 'client');
}
/**
* Perform some moodle validation.
*
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
global $DB;
$errors = parent::validation($data, $files);
$duplicate = $DB->get_field('user_info_category', 'id', ['name' => $data['name']]);
// Check the name is unique.
if (!empty($data['id'])) { // We are editing an existing record.
$olddata = $DB->get_record('user_info_category', ['id' => $data['id']]);
// Name has changed, new name in use, new name in use by another record.
$dupfound = (($olddata->name !== $data['name']) && $duplicate && ($data['id'] != $duplicate));
} else { // New profile category.
$dupfound = $duplicate;
}
if ($dupfound ) {
$errors['name'] = get_string('profilecategorynamenotunique', 'admin');
}
return $errors;
}
/**
* Returns context where this form is used
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
return \context_system::instance();
}
/**
* Checks if current user has access to this form, otherwise throws exception
*/
protected function check_access_for_dynamic_submission(): void {
require_capability('moodle/site:config', $this->get_context_for_dynamic_submission());
}
/**
* Process the form submission, used if form was submitted via AJAX
*/
public function process_dynamic_submission() {
global $CFG;
require_once($CFG->dirroot.'/user/profile/definelib.php');
profile_save_category($this->get_data());
}
/**
* Load in existing data as form defaults
*/
public function set_data_for_dynamic_submission(): void {
global $DB;
if ($id = $this->optional_param('id', 0, PARAM_INT)) {
$this->set_data($DB->get_record('user_info_category', ['id' => $id], '*', MUST_EXIST));
}
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$id = $this->optional_param('id', 0, PARAM_INT);
return new moodle_url('/user/profile/index.php',
['action' => 'editcategory', 'id' => $id]);
}
}
+183
View File
@@ -0,0 +1,183 @@
<?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_user\form;
use context;
use core_form\dynamic_form;
use moodle_url;
use profile_define_base;
/**
* Class field_form used for profile fields.
*
* @package core_user
* @copyright 2007 onwards Shane Elliot {@link http://pukunui.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class profile_field_form extends dynamic_form {
/** @var profile_define_base $field */
public $field;
/** @var \stdClass */
protected $fieldrecord;
/**
* Define the form
*/
public function definition() {
global $CFG;
require_once($CFG->dirroot.'/user/profile/definelib.php');
$mform = $this->_form;
// Everything else is dependant on the data type.
$datatype = $this->get_field_record()->datatype;
require_once($CFG->dirroot.'/user/profile/field/'.$datatype.'/define.class.php');
$newfield = 'profile_define_'.$datatype;
$this->field = new $newfield();
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'editfield');
$mform->setType('action', PARAM_ALPHANUMEXT);
$mform->addElement('hidden', 'datatype', $datatype);
$mform->setType('datatype', PARAM_ALPHA);
$this->field->define_form($mform);
}
/**
* Alter definition based on existing or submitted data
*/
public function definition_after_data() {
$mform = $this->_form;
$this->field->define_after_data($mform);
}
/**
* Perform some moodle validation.
* @param array $data
* @param array $files
* @return array
*/
public function validation($data, $files) {
return $this->field->define_validate($data, $files);
}
/**
* Returns the defined editors for the field.
* @return array
*/
public function editors(): array {
$editors = $this->field->define_editors();
return is_array($editors) ? $editors : [];
}
/**
* Returns context where this form is used
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
return \context_system::instance();
}
/**
* Checks if current user has access to this form, otherwise throws exception
*/
protected function check_access_for_dynamic_submission(): void {
require_capability('moodle/site:config', $this->get_context_for_dynamic_submission());
}
/**
* Process the form submission, used if form was submitted via AJAX
*/
public function process_dynamic_submission() {
global $CFG;
require_once($CFG->dirroot.'/user/profile/definelib.php');
profile_save_field($this->get_data(), $this->editors());
}
/**
* Load in existing data as form defaults
*/
public function set_data_for_dynamic_submission(): void {
$field = $this->get_field_record();
// Clean and prepare description for the editor.
$description = clean_text($field->description, $field->descriptionformat);
$field->description = ['text' => $description, 'format' => $field->descriptionformat, 'itemid' => 0];
// Convert the data format for.
if (is_array($this->editors())) {
foreach ($this->editors() as $editor) {
if (isset($field->$editor)) {
$editordesc = clean_text($field->$editor, $field->{$editor.'format'});
$field->$editor = ['text' => $editordesc, 'format' => $field->{$editor.'format'}, 'itemid' => 0];
}
}
}
$this->set_data($field);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$id = $this->optional_param('id', 0, PARAM_INT);
$datatype = $this->optional_param('datatype', 'text', PARAM_PLUGIN);
return new moodle_url('/user/profile/index.php',
['action' => 'editfield', 'id' => $id, 'datatype' => $id ? null : $datatype]);
}
/**
* Record for the field from the database (or generic record for a new field)
*
* @return false|mixed|\stdClass
* @throws \coding_exception
* @throws \dml_exception
*/
public function get_field_record() {
global $DB;
if (!$this->fieldrecord) {
$id = $this->optional_param('id', 0, PARAM_INT);
if (!$id || !($this->fieldrecord = $DB->get_record('user_info_field', ['id' => $id]))) {
$datatype = $this->optional_param('datatype', 'text', PARAM_PLUGIN);
$this->fieldrecord = new \stdClass();
$this->fieldrecord->datatype = $datatype;
$this->fieldrecord->description = '';
$this->fieldrecord->descriptionformat = FORMAT_HTML;
$this->fieldrecord->defaultdata = '';
$this->fieldrecord->defaultdataformat = FORMAT_HTML;
$this->fieldrecord->categoryid = $this->optional_param('categoryid', 0, PARAM_INT);
}
if (!\core_component::get_component_directory('profilefield_'.$this->fieldrecord->datatype)) {
throw new \moodle_exception('fieldnotfound', 'customfield');
}
}
return $this->fieldrecord;
}
}
@@ -0,0 +1,34 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_user\hook;
use core\hook\stoppable_trait;
/**
* Allow plugins to callback as soon possible after user has completed login.
*
* @package core_user
* @copyright 2024 Juan Leyva
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allow plugins to callback as soon possible after user has completed login.')]
#[\core\attribute\tags('user', 'login')]
class after_login_completed implements
\Psr\EventDispatcher\StoppableEventInterface
{
use stoppable_trait;
}
+46
View File
@@ -0,0 +1,46 @@
<?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_user\hook;
use stdClass;
use Psr\EventDispatcher\StoppableEventInterface;
/**
* Hook before user deletion.
*
* @package core_user
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions before a user is deleted.')]
#[\core\attribute\tags('user')]
class before_user_deleted implements
StoppableEventInterface
{
use \core\hook\stoppable_trait;
/**
* Constructor for the hook.
*
* @param stdClass $user The user instance
*/
public function __construct(
/** @var stdClass The user instance */
public readonly stdClass $user,
) {
}
}
+44
View File
@@ -0,0 +1,44 @@
<?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_user\hook;
use stdClass;
/**
* Hook before user information and data updates.
*
* @package core_user
* @copyright 2024 Safat Shahin <safat.shahin@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
#[\core\attribute\label('Allows plugins or features to perform actions before a user is updated.')]
#[\core\attribute\tags('user')]
class before_user_updated {
/**
* Constructor for the hook.
*
* @param stdClass $user The user instance
* @param stdClass $currentuserdata The old user instance
*/
public function __construct(
/** @var stdClass The user instance */
public readonly stdClass $user,
/** @var stdClass The old user instance */
public readonly stdClass $currentuserdata,
) {
}
}
@@ -0,0 +1,120 @@
<?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_user\hook;
use action_link;
use core\hook\described_hook;
use core\hook\deprecated_callback_replacement;
/**
* Class extend_bulk_user_actions
*
* @package core_user
* @copyright 2024 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class extend_bulk_user_actions implements deprecated_callback_replacement, described_hook {
/**
* Describes the hook purpose.
*
* @return string
*/
public static function get_hook_description(): string {
return 'Extend bulk user actions menu';
}
/**
* List of tags that describe this hook.
*
* @return string[]
*/
public static function get_hook_tags(): array {
return ['user'];
}
/**
* Returns list of lib.php plugin callbacks that were deprecated by the hook.
*
* @return array
*/
public static function get_deprecated_plugin_callbacks(): array {
return ['bulk_user_actions'];
}
/** @var array Stores all added user actions */
private $actions = [];
/**
* To be called by callback to add an action to the bulk user actions menu
*
* Callbacks with higher priority will be called first and actions they added will be displayed first.
* Callbacks with lower priority can override actions added by callbacks with higher priority.
*
* To prevent accidental overrides plugins should prefix the action identifier with the plugin name.
*
* @param string $identifier Unique key for the action, recommended to prefix with plugin name
* @param action_link|null $action an object containing the action URL and text,
* other properties are ignored. Can be set to null to remove an action added by somebody else.
* @param ?string $category Label for the option group in the action select dropdown
*/
public function add_action(string $identifier, ?action_link $action, ?string $category = null): void {
$category = $category ?? get_string('actions', 'moodle');
// If an action with the same identifier already exists in another option group, remove it.
$oldcategory = $this->find_action_category($identifier);
if ($oldcategory !== null && ($oldcategory !== $category || $action === null)) {
unset($this->actions[$oldcategory][$identifier]);
if (empty($this->actions[$oldcategory])) {
unset($this->actions[$oldcategory]);
}
}
// Add the new action.
if ($action !== null) {
$this->actions += [$category => []];
$this->actions[$category][$identifier] = $action;
}
}
/**
* Returns all actions groupped by category
*
* @return action_link[][]
*/
public function get_actions(): array {
return $this->actions;
}
/**
* Allows to locate an action by the identifier
*
* This method returns the option group label. The action itself can be found as:
* $category = $this->find_action_category($identifier);
* $action = $this->get_actions()[$category][$identifier];
*
* @param string $identifier
* @return string|null
*/
public function find_action_category(string $identifier): ?string {
foreach ($this->actions as $category => $actions) {
if (array_key_exists($identifier, $actions)) {
return $category;
}
}
return null;
}
}
+199
View File
@@ -0,0 +1,199 @@
<?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/>.
/**
* Defines a category in my profile page navigation.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines a category in my profile page navigation.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class category implements \renderable {
/**
* @var string Name of the category after which this category should appear.
*/
private $after;
/**
* @var string Name of the category.
*/
private $name;
/**
* @var string Title of the category.
*/
private $title;
/**
* @var node[] Array of nodes associated with this category.
*/
private $nodes = array();
/**
* @var string HTML class attribute for this category. Classes should be separated by a space, e.g. 'class1 class2'
*/
private $classes;
/**
* @var array list of properties publicly accessible via __get.
*/
private $properties = array('after', 'name', 'title', 'nodes', 'classes');
/**
* Constructor for category class.
*
* @param string $name Category name.
* @param string $title category title.
* @param null|string $after Name of category after which this category should appear.
* @param null|string $classes a list of css classes.
*/
public function __construct($name, $title, $after = null, $classes = null) {
$this->after = $after;
$this->name = $name;
$this->title = $title;
$this->classes = $classes;
}
/**
* Add a node to this category.
*
* @param node $node node object.
* @see \core_user\output\myprofile\tree::add_node()
*
* @throws \coding_exception
*/
public function add_node(node $node) {
$name = $node->name;
if (isset($this->nodes[$name])) {
throw new \coding_exception("Node with name $name already exists");
}
if ($node->parentcat !== $this->name) {
throw new \coding_exception("Node parent must match with the category it is added to");
}
$this->nodes[$node->name] = $node;
}
/**
* Sort nodes of the category in the order in which they should be displayed.
*
* @see \core_user\output\myprofile\tree::sort_categories()
* @throws \coding_exception
*/
public function sort_nodes() {
$tempnodes = array();
$this->validate_after_order();
// First content noes.
foreach ($this->nodes as $node) {
$after = $node->after;
$content = $node->content;
if (($after == null && !empty($content)) || $node->name === 'editprofile') {
// Can go anywhere in the cat. Also show content nodes first.
$tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node));
}
}
// Now nodes with no content.
foreach ($this->nodes as $node) {
$after = $node->after;
$content = $node->content;
if ($after == null && empty($content)) {
// Can go anywhere in the cat. Also show content nodes first.
$tempnodes = array_merge($tempnodes, array($node->name => $node), $this->find_nodes_after($node));
}
}
if (count($tempnodes) !== count($this->nodes)) {
// Orphan nodes found.
throw new \coding_exception('Some of the nodes specified contains invalid \'after\' property');
}
$this->nodes = $tempnodes;
}
/**
* Verifies that node with content can come after node with content only . Also verifies the same thing for nodes without
* content.
* @throws \coding_exception
*/
protected function validate_after_order() {
$nodearray = $this->nodes;
foreach ($this->nodes as $node) {
$after = $node->after;
if (!empty($after)) {
if (empty($nodearray[$after])) {
throw new \coding_exception('node {$node->name} specified contains invalid \'after\' property');
} else {
// Valid node found.
$afternode = $nodearray[$after];
$beforecontent = $node->content;
$aftercontent = $afternode->content;
if ((empty($beforecontent) && !empty($aftercontent)) || (!empty($beforecontent) && empty($aftercontent))) {
// Only node with content are allowed after content nodes. Same goes for no content nodes.
throw new \coding_exception('node {$node->name} specified contains invalid \'after\' property');
}
}
}
}
}
/**
* Given a node object find all node objects that should appear after it.
*
* @param node $node node object
*
* @return array
*/
protected function find_nodes_after($node) {
$return = array();
$nodearray = $this->nodes;
foreach ($nodearray as $nodeelement) {
if ($nodeelement->after === $node->name) {
// Find all nodes that comes after this node as well.
$return = array_merge($return, array($nodeelement->name => $nodeelement), $this->find_nodes_after($nodeelement));
}
}
return $return;
}
/**
* Magic get method.
*
* @param string $prop property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
}
+82
View File
@@ -0,0 +1,82 @@
<?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/>.
/**
* Defines Manager class for my profile navigation tree.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines MAnager class for myprofile navigation tree.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class manager {
/**
* Parse all callbacks and builds the tree.
*
* @param \stdClass $user user for which the profile is displayed.
* @param bool $iscurrentuser true if the profile being viewed is of current user, else false.
* @param \stdClass $course Course object
*
* @return tree Fully build tree to be rendered on my profile page.
*/
public static function build_tree($user, $iscurrentuser, $course = null) {
global $CFG;
$tree = new tree();
// Add core nodes.
require_once($CFG->libdir . "/myprofilelib.php");
core_myprofile_navigation($tree, $user, $iscurrentuser, $course);
// Core components.
$components = \core_component::get_core_subsystems();
foreach ($components as $component => $directory) {
if (empty($directory)) {
continue;
}
$file = $directory . "/lib.php";
if (is_readable($file)) {
require_once($file);
$function = "core_" . $component . "_myprofile_navigation";
if (function_exists($function)) {
$function($tree, $user, $iscurrentuser, $course);
}
}
}
// Plugins.
$pluginswithfunction = get_plugins_with_function('myprofile_navigation', 'lib.php');
foreach ($pluginswithfunction as $plugins) {
foreach ($plugins as $function) {
$function($tree, $user, $iscurrentuser, $course);
}
}
$tree->sort_categories();
return $tree;
}
}
+120
View File
@@ -0,0 +1,120 @@
<?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/>.
/**
* Defines a node in my profile page navigation.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines a node in my profile page navigation.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class node implements \renderable {
/**
* @var string Name of parent category.
*/
private $parentcat;
/**
* @var string Name of this node.
*/
private $name;
/**
* @var string Name of the node after which this node should appear.
*/
private $after;
/**
* @var string Title of this node.
*/
private $title;
/**
* @var string|\moodle_url Url that this node should link to.
*/
private $url;
/**
* @var string Content to display under this node.
*/
private $content;
/**
* @var string|\pix_icon Icon for this node.
*/
private $icon;
/**
* @var string HTML class attribute for this node. Classes should be separated by a space, e.g. 'class1 class2'
*/
private $classes;
/**
* @var array list of properties accessible via __get.
*/
private $properties = array('parentcat', 'after', 'name', 'title', 'url', 'content', 'icon', 'classes');
/**
* Constructor for the node.
*
* @param string $parentcat Name of parent category.
* @param string $name Name of this node.
* @param string $title Title of this node.
* @param null|string $after Name of the node after which this node should appear.
* @param null|string|\moodle_url $url Url that this node should link to.
* @param null|string $content Content to display under this node.
* @param null|string|\pix_icon $icon Icon for this node.
* @param null|string $classes a list of css classes.
*/
public function __construct($parentcat, $name, $title, $after = null, $url = null, $content = null, $icon = null,
$classes = null) {
$this->parentcat = $parentcat;
$this->after = $after;
$this->name = $name;
$this->title = $title;
$this->url = is_null($url) ? null : new \moodle_url($url);
$this->content = $content;
$this->icon = $icon;
$this->classes = $classes;
}
/**
* Magic get method.
*
* @param string $prop property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
}
+129
View File
@@ -0,0 +1,129 @@
<?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/>.
/**
* myprofile renderer.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die;
/**
* Report log renderer's for printing reports.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends \plugin_renderer_base {
/**
* Render the whole tree.
*
* @param tree $tree
*
* @return string
*/
public function render_tree(tree $tree) {
$return = \html_writer::start_tag('div', array('class' => 'profile_tree'));
$categories = $tree->categories;
foreach ($categories as $category) {
$return .= $this->render($category);
}
$return .= \html_writer::end_tag('div');
return $return;
}
/**
* Render a category.
*
* @param category $category
*
* @return string
*/
public function render_category(category $category) {
$classes = $category->classes;
if (empty($classes)) {
$return = \html_writer::start_tag('section',
array('class' => 'node_category card d-inline-block w-100 mb-3'));
$return .= \html_writer::start_tag('div', array('class' => 'card-body'));
} else {
$return = \html_writer::start_tag('section',
array('class' => 'node_category card d-inline-block w-100 mb-3' . $classes));
$return .= \html_writer::start_tag('div', array('class' => 'card-body'));
}
$return .= \html_writer::tag('h3', $category->title, array('class' => 'lead'));
$nodes = $category->nodes;
if (empty($nodes)) {
// No nodes, nothing to render.
return '';
}
$return .= \html_writer::start_tag('ul');
foreach ($nodes as $node) {
$return .= $this->render($node);
}
$return .= \html_writer::end_tag('ul');
$return .= \html_writer::end_tag('div');
$return .= \html_writer::end_tag('section');
return $return;
}
/**
* Render a node.
*
* @param node $node
*
* @return string
*/
public function render_node(node $node) {
$return = '';
if (is_object($node->url)) {
$header = \html_writer::link($node->url, $node->title);
} else {
$header = $node->title;
}
$icon = $node->icon;
if (!empty($icon)) {
$header .= $this->render($icon);
}
$content = $node->content;
$classes = $node->classes;
if (!empty($content)) {
if ($header) {
// There is some content to display below this make this a header.
$return = \html_writer::tag('dt', $header);
$return .= \html_writer::tag('dd', $content);
$return = \html_writer::tag('dl', $return);
} else {
$return = \html_writer::span($content);
}
if ($classes) {
$return = \html_writer::tag('li', $return, array('class' => 'contentnode ' . $classes));
} else {
$return = \html_writer::tag('li', $return, array('class' => 'contentnode'));
}
} else {
$return = \html_writer::span($header);
$return = \html_writer::tag('li', $return, array('class' => $classes));
}
return $return;
}
}
+158
View File
@@ -0,0 +1,158 @@
<?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/>.
/**
* Defines profile page navigation tree.
*
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output\myprofile;
defined('MOODLE_INTERNAL') || die();
/**
* Defines my profile page navigation tree.
*
* @since Moodle 2.9
* @package core_user
* @copyright 2015 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tree implements \renderable {
/**
* @var category[] Array of categories in the tree.
*/
private $categories = array();
/**
* @var node[] Array of nodes in the tree that were directly added to the tree.
*/
private $nodes = array();
/**
* @var array List of properties accessible via __get.
*/
private $properties = array('categories', 'nodes');
/**
* Add a node to the tree.
*
* @param node $node node object.
*
* @throws \coding_exception
*/
public function add_node(node $node) {
$name = $node->name;
if (isset($this->nodes[$name])) {
throw new \coding_exception("Node name $name already used");
}
$this->nodes[$node->name] = $node;
}
/**
* Add a category to the tree.
*
* @param category $cat category object.
*
* @throws \coding_exception
*/
public function add_category(category $cat) {
$name = $cat->name;
if (isset($this->categories[$name])) {
throw new \coding_exception("Category name $name already used");
}
$this->categories[$cat->name] = $cat;
}
/**
* Sort categories and nodes. Builds the tree structure that would be displayed to the user.
*
* @throws \coding_exception
*/
public function sort_categories() {
$this->attach_nodes_to_categories();
$tempcategories = array();
foreach ($this->categories as $category) {
$after = $category->after;
if ($after == null) {
// Can go anywhere in the tree.
$category->sort_nodes();
$tempcategories = array_merge($tempcategories, array($category->name => $category),
$this->find_categories_after($category));
}
}
if (count($tempcategories) !== count($this->categories)) {
// Orphan categories found.
throw new \coding_exception('Some of the categories specified contains invalid \'after\' property');
}
$this->categories = $tempcategories;
}
/**
* Attach various nodes to their respective categories.
*
* @throws \coding_exception
*/
protected function attach_nodes_to_categories() {
foreach ($this->nodes as $node) {
$parentcat = $node->parentcat;
if (!isset($this->categories[$parentcat])) {
throw new \coding_exception("Category $parentcat doesn't exist");
} else {
$this->categories[$parentcat]->add_node($node);
}
}
}
/**
* Find all category nodes that should be displayed after a given a category node.
*
* @param category $category category object
*
* @return category[] array of category objects
* @throws \coding_exception
*/
protected function find_categories_after($category) {
$return = array();
$categoryarray = $this->categories;
foreach ($categoryarray as $categoryelement) {
if ($categoryelement->after == $category->name) {
// Find all categories that comes after this category as well.
$categoryelement->sort_nodes();
$return = array_merge($return, array($categoryelement->name => $categoryelement),
$this->find_categories_after($categoryelement));
}
}
return $return;
}
/**
* Magic get method.
*
* @param string $prop property to get.
*
* @return mixed
* @throws \coding_exception
*/
public function __get($prop) {
if (in_array($prop, $this->properties)) {
return $this->$prop;
}
throw new \coding_exception('Property "' . $prop . '" doesn\'t exist');
}
}
+374
View File
@@ -0,0 +1,374 @@
<?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 for rendering user filters on the course participants page.
*
* @package core_user
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output;
use core_user\fields;
use renderer_base;
use stdClass;
/**
* Class for rendering user filters on the course participants page.
*
* @copyright 2020 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class participants_filter extends \core\output\datafilter {
/**
* Get data for all filter types.
*
* @return array
*/
protected function get_filtertypes(): array {
$filtertypes = [];
$filtertypes[] = $this->get_keyword_filter();
if ($filtertype = $this->get_enrolmentstatus_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_roles_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_enrolments_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_groups_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_accesssince_filter()) {
$filtertypes[] = $filtertype;
}
if ($filtertype = $this->get_country_filter()) {
$filtertypes[] = $filtertype;
}
return $filtertypes;
}
/**
* Get data for the enrolment status filter.
*
* @return stdClass|null
*/
protected function get_enrolmentstatus_filter(): ?stdClass {
if (!has_capability('moodle/course:enrolreview', $this->context)) {
return null;
}
return $this->get_filter_object(
'status',
get_string('participationstatus', 'core_enrol'),
false,
true,
null,
[
(object) [
'value' => ENROL_USER_ACTIVE,
'title' => get_string('active'),
],
(object) [
'value' => ENROL_USER_SUSPENDED,
'title' => get_string('inactive'),
],
]
);
}
/**
* Get data for the roles filter.
*
* @return stdClass|null
*/
protected function get_roles_filter(): ?stdClass {
$roles = [];
$roles += [-1 => get_string('noroles', 'role')];
$roles += get_viewable_roles($this->context, null, ROLENAME_BOTH);
if (has_capability('moodle/role:assign', $this->context)) {
$roles += get_assignable_roles($this->context, ROLENAME_BOTH);
}
return $this->get_filter_object(
'roles',
get_string('roles', 'core_role'),
false,
true,
null,
array_map(function($id, $title) {
return (object) [
'value' => $id,
'title' => $title,
];
}, array_keys($roles), array_values($roles))
);
}
/**
* Get data for the roles filter.
*
* @return stdClass|null
*/
protected function get_enrolments_filter(): ?stdClass {
if (!has_capability('moodle/course:enrolreview', $this->context)) {
return null;
}
if ($this->course->id == SITEID) {
// No enrolment methods for the site.
return null;
}
$instances = enrol_get_instances($this->course->id, true);
$plugins = enrol_get_plugins(false);
return $this->get_filter_object(
'enrolments',
get_string('enrolmentinstances', 'core_enrol'),
false,
true,
null,
array_filter(array_map(function($instance) use ($plugins): ?stdClass {
if (!array_key_exists($instance->enrol, $plugins)) {
return null;
}
return (object) [
'value' => $instance->id,
'title' => $plugins[$instance->enrol]->get_instance_name($instance),
];
}, array_values($instances)))
);
}
/**
* Get data for the groups filter.
*
* @return stdClass|null
*/
protected function get_groups_filter(): ?stdClass {
global $USER;
// Filter options for groups, if available.
$seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
$seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
if ($seeallgroups) {
$groups = [];
$groups += [USERSWITHOUTGROUP => (object) [
'id' => USERSWITHOUTGROUP,
'name' => get_string('nogroup', 'group'),
]];
$groups += groups_get_all_groups($this->course->id);
} else {
// Otherwise, just list the groups the user belongs to.
$groups = groups_get_all_groups($this->course->id, $USER->id);
}
// Return no data if no groups found (which includes if the only value is 'No group').
if (empty($groups) || (count($groups) === 1 && array_key_exists(-1, $groups))) {
return null;
}
return $this->get_filter_object(
'groups',
get_string('groups', 'core_group'),
false,
true,
null,
array_map(function($group) {
return (object) [
'value' => $group->id,
'title' => format_string($group->name, true, ['context' => $this->context]),
];
}, array_values($groups))
);
}
/**
* Get data for the accesssince filter.
*
* @return stdClass|null
*/
protected function get_accesssince_filter(): ?stdClass {
global $CFG, $DB;
$hiddenfields = [];
if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
}
if (array_key_exists('lastaccess', $hiddenfields)) {
return null;
}
// Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
// We need to make it diferently for normal courses and site course.
if (!($this->course->id == SITEID)) {
// Regular course.
$params = [
'courseid' => $this->course->id,
'timeaccess' => 0,
];
$select = 'courseid = :courseid AND timeaccess != :timeaccess';
$minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
// Determine enrolled users, who do not have accompanying lastaccess to the course.
[$enrolledsql, $enrolledparams] = get_enrolled_sql($this->context);
$sql = "SELECT 'x'
FROM {user} u
JOIN ({$enrolledsql}) je ON je.id = u.id
LEFT JOIN {user_lastaccess} ula ON ula.userid = je.id AND ula.courseid = :courseid
WHERE COALESCE(ula.timeaccess, 0) = :timeaccess";
$lastaccess0exists = $DB->record_exists_sql($sql, array_merge($params, $enrolledparams));
} else {
// Front page.
$params = ['lastaccess' => 0];
$select = 'lastaccess != :lastaccess';
$minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
$lastaccess0exists = $DB->record_exists('user', $params);
}
$now = usergetmidnight(time());
$getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
$values = [];
for ($i = 1; $i <= $count; $i++) {
$timestamp = strtotime("-{$i} {$type}", $now);
if ($timestamp < $minlastaccess) {
break;
}
if ($i === 1) {
$title = get_string("num{$singletype}", 'moodle', $i);
} else {
$title = get_string("num{$type}", 'moodle', $i);
}
$values[] = [
'value' => $timestamp,
'title' => $title,
];
}
return $values;
};
$values = array_merge(
$getoptions(6, 'day', 'days'),
$getoptions(10, 'week', 'weeks'),
$getoptions(11, 'month', 'months'),
$getoptions(1, 'year', 'years')
);
if ($lastaccess0exists) {
$values[] = [
'value' => -1,
'title' => get_string('never', 'moodle'),
];
}
if (count($values) <= 1) {
// Nothing to show.
return null;
}
return $this->get_filter_object(
'accesssince',
get_string('usersnoaccesssince'),
false,
false,
null,
$values
);
}
/**
* Get data for the country filter
*
* @return stdClass|null
*/
protected function get_country_filter(): ?stdClass {
$extrauserfields = fields::get_identity_fields($this->context, false);
if (array_search('country', $extrauserfields) === false) {
return null;
}
$countries = get_string_manager()->get_list_of_countries(true);
return $this->get_filter_object(
'country',
get_string('country'),
false,
true,
'core/datafilter/filtertypes/country',
array_map(function(string $code, string $name): stdClass {
return (object) [
'value' => $code,
'title' => $name,
];
}, array_keys($countries), array_values($countries))
);
}
/**
* Get data for the keywords filter.
*
* @return stdClass|null
*/
protected function get_keyword_filter(): ?stdClass {
return $this->get_filter_object(
'keywords',
get_string('filterbykeyword', 'core_user'),
true,
true,
'core/datafilter/filtertypes/keyword',
[],
true
);
}
/**
* Export the renderer data in a mustache template friendly format.
*
* @param renderer_base $output Unused.
* @return stdClass Data in a format compatible with a mustache template.
*/
public function export_for_template(renderer_base $output): stdClass {
return (object) [
'tableregionid' => $this->tableregionid,
'courseid' => $this->context->instanceid,
'filtertypes' => $this->get_filtertypes(),
'rownumber' => 1,
];
return $data;
}
}
+171
View File
@@ -0,0 +1,171 @@
<?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 containing the data necessary for rendering the status field in the course participants page.
*
* @package core_user
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use stdClass;
use templatable;
use user_enrolment_action;
/**
* Class containing the data for the status field.
*
* @package core_user
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class status_field implements renderable, templatable {
/** Active user enrolment status constant. */
const STATUS_ACTIVE = 0;
/** Suspended user enrolment status constant. */
const STATUS_SUSPENDED = 1;
/** Not current user enrolment status constant. */
const STATUS_NOT_CURRENT = 2;
/** @var string $enrolinstancename The enrolment instance name. */
protected $enrolinstancename;
/** @var string $coursename The course's full name. */
protected $coursename;
/** @var string $fullname The user's full name. */
protected $fullname;
/** @var string $status The user enrolment status. */
protected $status;
/** @var int $timestart The timestamp when the user's enrolment starts. */
protected $timestart;
/** @var int $timeend The timestamp when the user's enrolment ends. */
protected $timeend;
/** @var int $timeenrolled The timestamp when the user was enrolled. */
protected $timeenrolled;
/** @var user_enrolment_action[] $enrolactions Array of enrol action objects for the given enrolment method. */
protected $enrolactions;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as active. */
protected $statusactive = false;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as suspended. */
protected $statussuspended = false;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as not current. */
protected $statusnotcurrent = false;
/**
* status_field constructor.
*
* @param string $enrolinstancename The enrolment instance name.
* @param string $coursename The course's full name.
* @param string $fullname The user's full name.
* @param string $status The user enrolment status.
* @param int|null $timestart The timestamp when the user's enrolment starts.
* @param int|null $timeend The timestamp when the user's enrolment ends.
* @param user_enrolment_action[] $enrolactions Array of enrol action objects for the given enrolment method.
* @param int|null $timeenrolled The timestamp when the user was enrolled.
*/
public function __construct($enrolinstancename, $coursename, $fullname, $status, $timestart = null, $timeend = null,
$enrolactions = [], $timeenrolled = null) {
$this->enrolinstancename = $enrolinstancename;
$this->coursename = $coursename;
$this->fullname = $fullname;
$this->status = $status;
$this->timestart = $timestart;
$this->timeend = $timeend;
$this->enrolactions = $enrolactions;
$this->timeenrolled = $timeenrolled;
}
/**
* Function to export the renderer data in a format that is suitable for a
* mustache template. This means:
* 1. No complex types - only stdClass, array, int, string, float, bool
* 2. Any additional info that is required for the template is pre-calculated (e.g. capability checks).
*
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
* @return stdClass|array
*/
public function export_for_template(renderer_base $output) {
$data = new stdClass();
$data->enrolinstancename = $this->enrolinstancename;
$data->coursename = $this->coursename;
$data->fullname = $this->fullname;
$data->status = $this->status;
$data->active = $this->statusactive;
$data->suspended = $this->statussuspended;
$data->notcurrent = $this->statusnotcurrent;
if ($this->timestart) {
$data->timestart = userdate($this->timestart);
}
if ($this->timeend) {
$data->timeend = userdate($this->timeend);
}
if ($this->timeenrolled) {
$data->timeenrolled = userdate($this->timeenrolled);
}
$data->enrolactions = [];
foreach ($this->enrolactions as $enrolaction) {
$action = new stdClass();
$action->url = $enrolaction->get_url()->out(false);
$action->icon = $output->render($enrolaction->get_icon());
$action->attributes = [];
foreach ($enrolaction->get_attributes() as $name => $value) {
$attribute = (object) [
'name' => $name,
'value' => $value
];
$action->attributes[] = $attribute;
}
$data->enrolactions[] = $action;
}
return $data;
}
/**
* Status setter.
*
* @param int $status The user enrolment status representing one of this class' STATUS_* constants.
* @return status_field This class' instance. Useful for chaining.
*/
public function set_status($status = self::STATUS_ACTIVE) {
$this->statusactive = $status == static::STATUS_ACTIVE;
$this->statussuspended = $status == static::STATUS_SUSPENDED;
$this->statusnotcurrent = $status == static::STATUS_NOT_CURRENT;
return $this;
}
}
+236
View File
@@ -0,0 +1,236 @@
<?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_user\output;
use context_course;
use core_user;
use core_external\external_api;
use coding_exception;
/**
* Class to display list of user roles.
*
* @package core_user
* @copyright 2017 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_roles_editable extends \core\output\inplace_editable {
/** @var $context */
private $context = null;
/** @var \stdClass[] $courseroles */
private $courseroles;
/** @var \stdClass[] $profileroles */
private $profileroles;
/** @var \stdClass[] $viewableroles */
private $viewableroles;
/** @var \stdClass[] $assignableroles */
private $assignableroles;
/**
* Constructor.
*
* @param \stdClass $course The current course
* @param \context $context The course context
* @param \stdClass $user The current user
* @param \stdClass[] $courseroles The list of course roles.
* @param \stdClass[] $assignableroles The list of assignable roles in this course.
* @param \stdClass[] $profileroles The list of roles that should be visible in a users profile.
* @param \stdClass[] $userroles The list of user roles.
*/
public function __construct($course, $context, $user, $courseroles, $assignableroles, $profileroles, $userroles, $viewableroles = null) {
if ($viewableroles === null) {
debugging('Constructor for user_roles_editable now needs the result of get_viewable_roles passed as viewableroles');
}
// Check capabilities to get editable value.
$editable = has_capability('moodle/role:assign', $context);
// Invent an itemid.
$itemid = $course->id . ':' . $user->id;
$getrole = function($role) {
return $role->roleid;
};
$ids = array_values(array_unique(array_map($getrole, $userroles)));
$value = json_encode($ids);
// Remember these for the display value.
$this->courseroles = $courseroles;
$this->profileroles = $profileroles;
$this->viewableroles = array_keys($viewableroles);
$this->assignableroles = array_keys($assignableroles);
$this->context = $context;
parent::__construct('core_user', 'user_roles', $itemid, $editable, $value, $value);
// Removed the roles that were assigned to the user at a different context.
$options = $assignableroles;
foreach ($userroles as $role) {
if (isset($assignableroles[$role->roleid])) {
if ($role->contextid != $context->id) {
unset($options[$role->roleid]);
}
}
}
$fullname = htmlspecialchars(fullname($user), ENT_QUOTES, 'utf-8');
$this->edithint = get_string('xroleassignments', 'role', $fullname);
$this->editlabel = get_string('xroleassignments', 'role', $fullname);
$attributes = ['multiple' => true];
$this->set_type_autocomplete($options, $attributes);
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param \renderer_base $output
* @return array
*/
public function export_for_template(\renderer_base $output) {
$listofroles = [];
$roleids = json_decode($this->value);
$viewableroleids = array_intersect($roleids, array_merge($this->viewableroles, $this->assignableroles));
foreach ($viewableroleids as $id) {
// If this is a student, we only show a subset of the roles.
if ($this->editable || array_key_exists($id, $this->profileroles)) {
$listofroles[] = format_string($this->courseroles[$id]->localname, true, ['context' => $this->context]);
}
}
if (!empty($listofroles)) {
$this->displayvalue = implode(', ', $listofroles);
} else if (!empty($roleids) && empty($viewableroleids)) {
$this->displayvalue = get_string('novisibleroles', 'role');
} else {
$this->displayvalue = get_string('noroles', 'role');
}
return parent::export_for_template($output);
}
/**
* Updates the value in database and returns itself, called from inplace_editable callback
*
* @param int $itemid
* @param mixed $newvalue
* @return \self
*/
public static function update($itemid, $newvalue) {
global $DB;
// Check caps.
// Do the thing.
// Return one of me.
// Validate the inputs.
list($courseid, $userid) = explode(':', $itemid, 2);
$courseid = clean_param($courseid, PARAM_INT);
$userid = clean_param($userid, PARAM_INT);
$roleids = json_decode($newvalue);
foreach ($roleids as $index => $roleid) {
$roleids[$index] = clean_param($roleid, PARAM_INT);
}
// Check user is enrolled in the course.
$context = context_course::instance($courseid);
external_api::validate_context($context);
// Check permissions.
require_capability('moodle/role:assign', $context);
if (!is_enrolled($context, $userid)) {
throw new coding_exception('User does not belong to the course');
}
// Check that all the groups belong to the course.
$allroles = role_fix_names(get_all_roles($context), $context, ROLENAME_BOTH);
$assignableroles = get_assignable_roles($context, ROLENAME_BOTH, false);
$viewableroles = get_viewable_roles($context);
$userrolesbyid = get_user_roles($context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
$profileroles = get_profile_roles($context);
// Set an array where the index is the roleid.
$userroles = array();
foreach ($userrolesbyid as $id => $role) {
$userroles[$role->roleid] = $role;
}
$rolestoprocess = [];
foreach ($roleids as $roleid) {
if (!isset($assignableroles[$roleid])) {
throw new coding_exception('Role cannot be assigned in this course.');
}
$rolestoprocess[$roleid] = $roleid;
}
// Process adds.
foreach ($rolestoprocess as $roleid) {
if (!isset($userroles[$roleid])) {
// Add them.
$id = role_assign($roleid, $userid, $context);
// Keep this variable in sync.
$role = new \stdClass();
$role->id = $id;
$role->roleid = $roleid;
$role->contextid = $context->id;
$userroles[$role->roleid] = $role;
}
}
// Process removals.
foreach ($assignableroles as $roleid => $rolename) {
if (isset($userroles[$roleid]) && !isset($rolestoprocess[$roleid])) {
// Do not remove the role if we are not in the same context.
if ($userroles[$roleid]->contextid != $context->id) {
continue;
}
$ras = $DB->get_records('role_assignments', ['contextid' => $context->id, 'userid' => $userid,
'roleid' => $roleid]);
$allremoved = true;
foreach ($ras as $ra) {
if ($ra->component) {
if (strpos($ra->component, 'enrol_') !== 0) {
continue;
}
if (!$plugin = enrol_get_plugin(substr($ra->component, 6))) {
continue;
}
if ($plugin->roles_protected()) {
$allremoved = false;
continue;
}
}
role_unassign($ra->roleid, $ra->userid, $ra->contextid, $ra->component, $ra->itemid);
}
if ($allremoved) {
unset($userroles[$roleid]);
}
}
}
$course = get_course($courseid);
$user = core_user::get_user($userid);
return new self($course, $context, $user, $allroles, $assignableroles, $profileroles, $userroles, $viewableroles);
}
}
+565
View File
@@ -0,0 +1,565 @@
<?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_user
* @copyright 2018 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\privacy;
defined('MOODLE_INTERNAL') || die();
use \core_privacy\local\metadata\collection;
use \core_privacy\local\request\transform;
use \core_privacy\local\request\contextlist;
use \core_privacy\local\request\approved_contextlist;
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.
*
* @package core_comment
* @copyright 2018 Adrian Greeve <adrian@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,
\core_privacy\local\request\user_preference_provider {
/**
* Returns information about the user data stored in this component.
*
* @param collection $collection A list of information about this component
* @return collection The collection object filled out with information about this component.
*/
public static function get_metadata(collection $collection): collection {
$userfields = [
'id' => 'privacy:metadata:id',
'auth' => 'privacy:metadata:auth',
'confirmed' => 'privacy:metadata:confirmed',
'policyagreed' => 'privacy:metadata:policyagreed',
'deleted' => 'privacy:metadata:deleted',
'suspended' => 'privacy:metadata:suspended',
'mnethostid' => 'privacy:metadata:mnethostid',
'username' => 'privacy:metadata:username',
'password' => 'privacy:metadata:password',
'idnumber' => 'privacy:metadata:idnumber',
'firstname' => 'privacy:metadata:firstname',
'lastname' => 'privacy:metadata:lastname',
'email' => 'privacy:metadata:email',
'emailstop' => 'privacy:metadata:emailstop',
'phone1' => 'privacy:metadata:phone',
'phone2' => 'privacy:metadata:phone',
'institution' => 'privacy:metadata:institution',
'department' => 'privacy:metadata:department',
'address' => 'privacy:metadata:address',
'city' => 'privacy:metadata:city',
'country' => 'privacy:metadata:country',
'lang' => 'privacy:metadata:lang',
'calendartype' => 'privacy:metadata:calendartype',
'theme' => 'privacy:metadata:theme',
'timezone' => 'privacy:metadata:timezone',
'firstaccess' => 'privacy:metadata:firstaccess',
'lastaccess' => 'privacy:metadata:lastaccess',
'lastlogin' => 'privacy:metadata:lastlogin',
'currentlogin' => 'privacy:metadata:currentlogin',
'lastip' => 'privacy:metadata:lastip',
'secret' => 'privacy:metadata:secret',
'picture' => 'privacy:metadata:picture',
'description' => 'privacy:metadata:description',
'maildigest' => 'privacy:metadata:maildigest',
'maildisplay' => 'privacy:metadata:maildisplay',
'autosubscribe' => 'privacy:metadata:autosubscribe',
'trackforums' => 'privacy:metadata:trackforums',
'timecreated' => 'privacy:metadata:timecreated',
'timemodified' => 'privacy:metadata:timemodified',
'trustbitmask' => 'privacy:metadata:trustbitmask',
'imagealt' => 'privacy:metadata:imagealt',
'lastnamephonetic' => 'privacy:metadata:lastnamephonetic',
'firstnamephonetic' => 'privacy:metadata:firstnamephonetic',
'middlename' => 'privacy:metadata:middlename',
'alternatename' => 'privacy:metadata:alternatename',
'moodlenetprofile' => 'privacy:metadata:moodlenetprofile'
];
$passwordhistory = [
'userid' => 'privacy:metadata:userid',
'hash' => 'privacy:metadata:hash',
'timecreated' => 'privacy:metadata:timecreated'
];
$lastaccess = [
'userid' => 'privacy:metadata:userid',
'courseid' => 'privacy:metadata:courseid',
'timeaccess' => 'privacy:metadata:timeaccess'
];
$userpasswordresets = [
'userid' => 'privacy:metadata:userid',
'timerequested' => 'privacy:metadata:timerequested',
'timererequested' => 'privacy:metadata:timererequested',
'token' => 'privacy:metadata:token'
];
$userdevices = [
'userid' => 'privacy:metadata:userid',
'appid' => 'privacy:metadata:appid',
'name' => 'privacy:metadata:devicename',
'model' => 'privacy:metadata:model',
'platform' => 'privacy:metadata:platform',
'version' => 'privacy:metadata:version',
'pushid' => 'privacy:metadata:pushid',
'uuid' => 'privacy:metadata:uuid',
'timecreated' => 'privacy:metadata:timecreated',
'timemodified' => 'privacy:metadata:timemodified'
];
$usersessions = [
'state' => 'privacy:metadata:state',
'sid' => 'privacy:metadata:sid',
'userid' => 'privacy:metadata:userid',
'sessdata' => 'privacy:metadata:sessdata',
'timecreated' => 'privacy:metadata:timecreated',
'timemodified' => 'privacy:metadata:timemodified',
'firstip' => 'privacy:metadata:firstip',
'lastip' => 'privacy:metadata:lastip'
];
$courserequest = [
'fullname' => 'privacy:metadata:fullname',
'shortname' => 'privacy:metadata:shortname',
'summary' => 'privacy:metadata:summary',
'category' => 'privacy:metadata:category',
'reason' => 'privacy:metadata:reason',
'requester' => 'privacy:metadata:requester'
];
$mypages = [
'userid' => 'privacy:metadata:my_pages:userid',
'name' => 'privacy:metadata:my_pages:name',
'private' => 'privacy:metadata:my_pages:private',
];
$userpreferences = [
'userid' => 'privacy:metadata:user_preferences:userid',
'name' => 'privacy:metadata:user_preferences:name',
'value' => 'privacy:metadata:user_preferences:value'
];
$collection->add_database_table('user', $userfields, 'privacy:metadata:usertablesummary');
$collection->add_database_table('user_password_history', $passwordhistory, 'privacy:metadata:passwordtablesummary');
$collection->add_database_table('user_password_resets', $userpasswordresets, 'privacy:metadata:passwordresettablesummary');
$collection->add_database_table('user_lastaccess', $lastaccess, 'privacy:metadata:lastaccesstablesummary');
$collection->add_database_table('user_devices', $userdevices, 'privacy:metadata:devicetablesummary');
$collection->add_database_table('course_request', $courserequest, 'privacy:metadata:requestsummary');
$collection->add_database_table('sessions', $usersessions, 'privacy:metadata:sessiontablesummary');
$collection->add_database_table('my_pages', $mypages, 'privacy:metadata:my_pages');
$collection->add_database_table('user_preferences', $userpreferences, 'privacy:metadata:user_preferences');
$collection->add_subsystem_link('core_files', [], 'privacy:metadata:filelink');
$collection->add_user_preference(
'core_user_welcome',
'privacy:metadata:user_preference:core_user_welcome'
);
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 {
$params = ['userid' => $userid, 'contextuser' => CONTEXT_USER];
$sql = "SELECT id
FROM {context}
WHERE instanceid = :userid and contextlevel = :contextuser";
$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;
}
$userlist->add_user($context->instanceid);
}
/**
* 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) {
$context = $contextlist->current();
$user = \core_user::get_user($contextlist->get_user()->id);
static::export_user($user, $context);
static::export_password_history($user->id, $context);
static::export_password_resets($user->id, $context);
static::export_lastaccess($user->id, $context);
static::export_course_requests($user->id, $context);
static::export_user_devices($user->id, $context);
static::export_user_session_data($user->id, $context);
}
/**
* Delete all data for all users in the specified context.
*
* @param context $context The specific context to delete data for.
*/
public static function delete_data_for_all_users_in_context(\context $context) {
// Only delete data for a user context.
if ($context->contextlevel == CONTEXT_USER) {
static::delete_user_data($context->instanceid, $context);
}
}
/**
* 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) {
static::delete_user_data($context->instanceid, $context);
}
}
/**
* 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) {
foreach ($contextlist as $context) {
// Let's be super certain that we have the right information for this user here.
if ($context->contextlevel == CONTEXT_USER && $contextlist->get_user()->id == $context->instanceid) {
static::delete_user_data($contextlist->get_user()->id, $contextlist->current());
}
}
}
/**
* Deletes non vital information about a user.
*
* @param int $userid The user ID to delete
* @param \context $context The user context
*/
protected static function delete_user_data(int $userid, \context $context) {
global $DB;
// Delete password history.
$DB->delete_records('user_password_history', ['userid' => $userid]);
// Delete last access.
$DB->delete_records('user_lastaccess', ['userid' => $userid]);
// Delete password resets.
$DB->delete_records('user_password_resets', ['userid' => $userid]);
// Delete user devices.
$DB->delete_records('user_devices', ['userid' => $userid]);
// Delete user course requests.
$DB->delete_records('course_request', ['requester' => $userid]);
// Delete sessions.
$DB->delete_records('sessions', ['userid' => $userid]);
// Do I delete user preferences? Seems like the right place to do it.
$DB->delete_records('user_preferences', ['userid' => $userid]);
// Delete all of the files for this user.
$fs = get_file_storage();
$fs->delete_area_files($context->id, 'user');
// For the user record itself we only want to remove unnecessary data. We still need the core data to keep as a record
// that we actually did follow the request to be forgotten.
$user = \core_user::get_user($userid);
// Update fields we wish to change to nothing.
$user->deleted = 1;
$user->idnumber = '';
$user->emailstop = 0;
$user->phone1 = '';
$user->phone2 = '';
$user->institution = '';
$user->department = '';
$user->address = '';
$user->city = '';
$user->country = '';
$user->lang = '';
$user->calendartype = '';
$user->theme = '';
$user->timezone = '';
$user->firstaccess = 0;
$user->lastaccess = 0;
$user->lastlogin = 0;
$user->currentlogin = 0;
$user->lastip = 0;
$user->secret = '';
$user->picture = '';
$user->description = '';
$user->descriptionformat = 0;
$user->mailformat = 0;
$user->maildigest = 0;
$user->maildisplay = 0;
$user->autosubscribe = 0;
$user->trackforums = 0;
$user->timecreated = 0;
$user->timemodified = 0;
$user->trustbitmask = 0;
$user->imagealt = '';
$user->lastnamephonetic = '';
$user->firstnamephonetic = '';
$user->middlename = '';
$user->alternatename = '';
$DB->update_record('user', $user);
}
/**
* Export core user data.
*
* @param \stdClass $user The user object.
* @param \context $context The user context.
*/
protected static function export_user(\stdClass $user, \context $context) {
$data = (object) [
'auth' => $user->auth,
'confirmed' => transform::yesno($user->confirmed),
'policyagreed' => transform::yesno($user->policyagreed),
'deleted' => transform::yesno($user->deleted),
'suspended' => transform::yesno($user->suspended),
'username' => $user->username,
'idnumber' => $user->idnumber,
'firstname' => format_string($user->firstname, true, ['context' => $context]),
'lastname' => format_string($user->lastname, true, ['context' => $context]),
'email' => $user->email,
'emailstop' => transform::yesno($user->emailstop),
'phone1' => format_string($user->phone1, true, ['context' => $context]),
'phone2' => format_string($user->phone2, true, ['context' => $context]),
'institution' => format_string($user->institution, true, ['context' => $context]),
'department' => format_string($user->department, true, ['context' => $context]),
'address' => format_string($user->address, true, ['context' => $context]),
'city' => format_string($user->city, true, ['context' => $context]),
'country' => format_string($user->country, true, ['context' => $context]),
'lang' => $user->lang,
'calendartype' => $user->calendartype,
'theme' => $user->theme,
'timezone' => $user->timezone,
'firstaccess' => $user->firstaccess ? transform::datetime($user->firstaccess) : null,
'lastaccess' => $user->lastaccess ? transform::datetime($user->lastaccess) : null,
'lastlogin' => $user->lastlogin ? transform::datetime($user->lastlogin) : null,
'currentlogin' => $user->currentlogin ? transform::datetime($user->currentlogin) : null,
'lastip' => $user->lastip,
'secret' => $user->secret,
'picture' => $user->picture,
'description' => format_text(
writer::with_context($context)->rewrite_pluginfile_urls(
[],
'user',
'profile',
'',
$user->description
), $user->descriptionformat, ['context' => $context]),
'maildigest' => transform::yesno($user->maildigest),
'maildisplay' => $user->maildisplay,
'autosubscribe' => transform::yesno($user->autosubscribe),
'trackforums' => transform::yesno($user->trackforums),
'timecreated' => transform::datetime($user->timecreated),
'timemodified' => transform::datetime($user->timemodified),
'imagealt' => format_string($user->imagealt, true, ['context' => $context]),
'lastnamephonetic' => format_string($user->lastnamephonetic, true, ['context' => $context]),
'firstnamephonetic' => format_string($user->firstnamephonetic, true, ['context' => $context]),
'middlename' => format_string($user->middlename, true, ['context' => $context]),
'alternatename' => format_string($user->alternatename, true, ['context' => $context])
];
writer::with_context($context)->export_area_files([], 'user', 'profile', 0)
->export_data([], $data);
// Export profile images.
writer::with_context($context)->export_area_files([get_string('privacy:profileimagespath', 'user')], 'user', 'icon', 0);
// Export private files.
writer::with_context($context)->export_area_files([get_string('privacy:privatefilespath', 'user')], 'user', 'private', 0);
// Export draft files.
writer::with_context($context)->export_area_files([get_string('privacy:draftfilespath', 'user')], 'user', 'draft', false);
}
/**
* Export information about the last time a user accessed a course.
*
* @param int $userid The user ID.
* @param \context $context The user context.
*/
protected static function export_lastaccess(int $userid, \context $context) {
global $DB;
$sql = "SELECT c.id, c.fullname, ul.timeaccess
FROM {user_lastaccess} ul
JOIN {course} c ON c.id = ul.courseid
WHERE ul.userid = :userid";
$params = ['userid' => $userid];
$records = $DB->get_records_sql($sql, $params);
if (!empty($records)) {
$lastaccess = (object) array_map(function($record) use ($context) {
return [
'course_name' => format_string($record->fullname, true, ['context' => $context]),
'timeaccess' => transform::datetime($record->timeaccess)
];
}, $records);
writer::with_context($context)->export_data([get_string('privacy:lastaccesspath', 'user')], $lastaccess);
}
}
/**
* Exports information about password resets.
*
* @param int $userid The user ID
* @param \context $context Context for this user.
*/
protected static function export_password_resets(int $userid, \context $context) {
global $DB;
$records = $DB->get_records('user_password_resets', ['userid' => $userid]);
if (!empty($records)) {
$passwordresets = (object) array_map(function($record) {
return [
'timerequested' => transform::datetime($record->timerequested),
'timererequested' => transform::datetime($record->timererequested)
];
}, $records);
writer::with_context($context)->export_data([get_string('privacy:passwordresetpath', 'user')], $passwordresets);
}
}
/**
* Exports information about the user's mobile devices.
*
* @param int $userid The user ID.
* @param \context $context Context for this user.
*/
protected static function export_user_devices(int $userid, \context $context) {
global $DB;
$records = $DB->get_records('user_devices', ['userid' => $userid]);
if (!empty($records)) {
$userdevices = (object) array_map(function($record) {
return [
'appid' => $record->appid,
'name' => $record->name,
'model' => $record->model,
'platform' => $record->platform,
'version' => $record->version,
'timecreated' => transform::datetime($record->timecreated),
'timemodified' => transform::datetime($record->timemodified)
];
}, $records);
writer::with_context($context)->export_data([get_string('privacy:devicespath', 'user')], $userdevices);
}
}
/**
* Exports information about course requests this user made.
*
* @param int $userid The user ID.
* @param \context $context The context object
*/
protected static function export_course_requests(int $userid, \context $context) {
global $DB;
$sql = "SELECT cr.shortname, cr.fullname, cr.summary, cc.name AS category, cr.reason
FROM {course_request} cr
JOIN {course_categories} cc ON cr.category = cc.id
WHERE cr.requester = :userid";
$params = ['userid' => $userid];
$records = $DB->get_records_sql($sql, $params);
if ($records) {
writer::with_context($context)->export_data([get_string('privacy:courserequestpath', 'user')], (object) $records);
}
}
/**
* Get details about the user's password history.
*
* @param int $userid The user ID that we are getting the password history for.
* @param \context $context the user context.
*/
protected static function export_password_history(int $userid, \context $context) {
global $DB;
// Just provide a count of how many entries we have.
$recordcount = $DB->count_records('user_password_history', ['userid' => $userid]);
if ($recordcount) {
$passwordhistory = (object) ['password_history_count' => $recordcount];
writer::with_context($context)->export_data([get_string('privacy:passwordhistorypath', 'user')], $passwordhistory);
}
}
/**
* Exports information about the user's session.
*
* @param int $userid The user ID.
* @param \context $context The context for this user.
*/
protected static function export_user_session_data(int $userid, \context $context) {
global $DB, $SESSION;
$records = $DB->get_records('sessions', ['userid' => $userid]);
if (!empty($records)) {
$sessiondata = (object) array_map(function($record) {
return [
'state' => $record->state,
'sessdata' => ($record->sessdata !== null) ? base64_decode($record->sessdata) : '',
'timecreated' => transform::datetime($record->timecreated),
'timemodified' => transform::datetime($record->timemodified),
'firstip' => $record->firstip,
'lastip' => $record->lastip
];
}, $records);
writer::with_context($context)->export_data([get_string('privacy:sessionpath', 'user')], $sessiondata);
}
}
/**
* Export all user preferences for the plugin.
*
* @param int $userid The userid of the user whose data is to be exported.
*/
public static function export_user_preferences(int $userid) {
$userwelcomepreference = get_user_preferences('core_user_welcome', null, $userid);
if ($userwelcomepreference !== null) {
writer::export_user_preference(
'core_user',
'core_user_welcome',
$userwelcomepreference,
get_string('privacy:metadata:user_preference:core_user_welcome', 'core_user')
);
}
}
}
@@ -0,0 +1,140 @@
<?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/>.
declare(strict_types=1);
namespace core_user\reportbuilder\datasource;
use lang_string;
use core_cohort\reportbuilder\local\entities\cohort;
use core_reportbuilder\datasource;
use core_reportbuilder\local\entities\user;
use core_reportbuilder\local\filters\boolean_select;
use core_reportbuilder\local\helpers\database;
use core_tag\reportbuilder\local\entities\tag;
/**
* Users datasource
*
* @package core_user
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class users extends datasource {
/**
* Return user friendly name of the datasource
*
* @return string
*/
public static function get_name(): string {
return get_string('users');
}
/**
* Initialise report
*/
protected function initialise(): void {
global $CFG;
$userentity = new user();
$useralias = $userentity->get_table_alias('user');
$this->set_main_table('user', $useralias);
$this->add_entity($userentity);
$userparamguest = database::generate_param_name();
$this->add_base_condition_sql("{$useralias}.id != :{$userparamguest} AND {$useralias}.deleted = 0", [
$userparamguest => $CFG->siteguest,
]);
// Join the tag entity.
$tagentity = (new tag())
->set_table_alias('tag', $userentity->get_table_alias('tag'))
->set_entity_title(new lang_string('interests'));
$this->add_entity($tagentity
->add_joins($userentity->get_tag_joins()));
// Join the cohort entity.
$cohortentity = new cohort();
$cohortalias = $cohortentity->get_table_alias('cohort');
$cohortmemberalias = database::generate_alias();
$this->add_entity($cohortentity->add_joins([
"LEFT JOIN {cohort_members} {$cohortmemberalias} ON {$cohortmemberalias}.userid = {$useralias}.id",
"LEFT JOIN {cohort} {$cohortalias} ON {$cohortalias}.id = {$cohortmemberalias}.cohortid",
]));
// Add all columns/filters/conditions from entities to be available in custom reports.
$this->add_all_from_entity($userentity->get_entity_name());
$this->add_all_from_entity($tagentity->get_entity_name(), ['name', 'namewithlink'], ['name'], ['name']);
$this->add_all_from_entity($cohortentity->get_entity_name(), ['name', 'idnumber', 'description', 'customfield*'],
['cohortselect', 'name', 'idnumber', 'customfield*'], ['cohortselect', 'name', 'idnumber', 'customfield*']);
}
/**
* Return the columns that will be added to the report once is created
*
* @return string[]
*/
public function get_default_columns(): array {
return ['user:fullname', 'user:username', 'user:email'];
}
/**
* Return the filters that will be added to the report once is created
*
* @return string[]
*/
public function get_default_filters(): array {
return ['user:fullname', 'user:username', 'user:email'];
}
/**
* Return the conditions that will be added to the report once is created
*
* @return string[]
*/
public function get_default_conditions(): array {
return [
'user:fullname',
'user:username',
'user:email',
'user:suspended',
];
}
/**
* Return the conditions values that will be added to the report once is created
*
* @return array
*/
public function get_default_condition_values(): array {
return [
'user:suspended_operator' => boolean_select::NOT_CHECKED,
];
}
/**
* Return the default sorting that will be added to the report once it is created
*
* @return int[]
*/
public function get_default_column_sorting(): array {
return [
'user:fullname' => SORT_ASC,
];
}
}
+218
View File
@@ -0,0 +1,218 @@
<?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/>.
/**
* Index teachers in a course
*
* @package core_user
* @author Nathan Nguyen <nathannguyen@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\search;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/user/lib.php');
/**
* Search for user role assignment in a course
*
* @package core_user
* @author Nathan Nguyen <nathannguyen@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class course_teacher extends \core_search\base {
/**
* The context levels the search implementation is working on.
*
* @var array
*/
protected static $levels = [CONTEXT_COURSE];
/**
* Returns the moodle component name.
*
* It might be the plugin name (whole frankenstyle name) or the core subsystem name.
*
* @return string
*/
public function get_component_name() {
return 'course_teacher';
}
/**
* Returns recordset containing required data attributes for indexing.
*
* @param number $modifiedfrom
* @param \context|null $context Optional context to restrict scope of returned results
* @return \moodle_recordset|null Recordset (or null if no results)
*/
public function get_document_recordset($modifiedfrom = 0, \context $context = null) {
global $DB;
$teacherroleids = get_config('core', 'searchteacherroles');
// Only index teacher roles.
if (!empty($teacherroleids)) {
$teacherroleids = explode(',', $teacherroleids);
list($insql, $inparams) = $DB->get_in_or_equal($teacherroleids, SQL_PARAMS_NAMED);
} else {
// Do not index at all.
list($insql, $inparams) = [' = :roleid', ['roleid' => 0]];
}
$params = [
'coursecontext' => CONTEXT_COURSE,
'modifiedfrom' => $modifiedfrom
];
$params = array_merge($params, $inparams);
$recordset = $DB->get_recordset_sql("
SELECT u.*, ra.contextid, r.shortname as roleshortname, ra.id as itemid, ra.timemodified as timeassigned
FROM {role_assignments} ra
JOIN {context} ctx
ON ctx.id = ra.contextid
AND ctx.contextlevel = :coursecontext
JOIN {user} u
ON u.id = ra.userid
JOIN {role} r
ON r.id = ra.roleid
WHERE ra.timemodified >= :modifiedfrom AND r.id $insql
ORDER BY ra.timemodified ASC", $params);
return $recordset;
}
/**
* Returns document instances for each record in the recordset.
*
* @param \stdClass $record
* @param array $options
* @return \core_search\document
*/
public function get_document($record, $options = array()) {
$context = \context::instance_by_id($record->contextid);
// Content.
if ($context->contextlevel == CONTEXT_COURSE) {
$course = get_course($context->instanceid);
$contentdata = new \stdClass();
$contentdata->role = ucfirst($record->roleshortname);
$contentdata->course = $course->fullname;
$content = get_string('content:courserole', 'core_search', $contentdata);
} else {
return false;
}
$doc = \core_search\document_factory::instance($record->itemid, $this->componentname, $this->areaname);
// Assigning properties to our document.
$doc->set('title', content_to_text(fullname($record), false));
$doc->set('contextid', $context->id);
$doc->set('courseid', $context->instanceid);
$doc->set('itemid', $record->itemid);
$doc->set('modified', $record->timeassigned);
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
$doc->set('userid', $record->id);
$doc->set('content', $content);
// Check if this document should be considered new.
if (isset($options['lastindexedtime']) && $options['lastindexedtime'] < $record->timeassigned) {
$doc->set_is_new(true);
}
return $doc;
}
/**
* Checking whether I can access a document
*
* @param int $id user id
* @return int
*/
public function check_access($id) {
$user = $this->get_user($id);
if (!$user || $user->deleted) {
return \core_search\manager::ACCESS_DELETED;
}
if (user_can_view_profile($user)) {
return \core_search\manager::ACCESS_GRANTED;
}
return \core_search\manager::ACCESS_DENIED;
}
/**
* Returns a url to the document context.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_context_url(\core_search\document $doc) {
$user = $this->get_user($doc->get('itemid'));
$courseid = $doc->get('courseid');
return new \moodle_url('/user/view.php', array('id' => $user->id, 'course' => $courseid));
}
/**
* Returns the user fullname to display as document title
*
* @param \core_search\document $doc
* @return string User fullname
*/
public function get_document_display_title(\core_search\document $doc) {
$user = $this->get_user($doc->get('itemid'));
return fullname($user);
}
/**
* Get user based on role assignment id
*
* @param int $itemid role assignment id
* @return mixed
*/
private function get_user($itemid) {
global $DB;
$sql = "SELECT u.*
FROM {user} u
JOIN {role_assignments} ra
ON ra.userid = u.id
WHERE ra.id = :raid";
return $DB->get_record_sql($sql, array('raid' => $itemid));
}
/**
* Returns a list of category names associated with the area.
*
* @return array
*/
public function get_category_names() {
return [\core_search\manager::SEARCH_AREA_CATEGORY_ALL, \core_search\manager::SEARCH_AREA_CATEGORY_USERS];
}
/**
* Link to the teacher in the course
*
* @param \core_search\document $doc the document
* @return \moodle_url
*/
public function get_doc_url(\core_search\document $doc) {
return $this->get_context_url($doc);
}
}
+228
View File
@@ -0,0 +1,228 @@
<?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/>.
/**
* Search area for Users for whom I have authority to view profile.
*
* @package core_user
* @copyright 2016 Devang Gaur {@link http://www.devanggaur.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\search;
require_once($CFG->dirroot . '/user/lib.php');
defined('MOODLE_INTERNAL') || die();
/**
* Search area for Users for whom I have access to view profile.
*
* @package core_user
* @copyright 2016 Devang Gaur {@link http://www.devanggaur.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user extends \core_search\base {
/**
* Returns recordset containing required data attributes for indexing.
*
* @param number $modifiedfrom
* @param \context|null $context Optional context to restrict scope of returned results
* @return \moodle_recordset|null Recordset (or null if no results)
*/
public function get_document_recordset($modifiedfrom = 0, \context $context = null) {
global $DB;
// Prepare query conditions.
$where = 'timemodified >= ? AND deleted = ? AND confirmed = ?';
$params = [$modifiedfrom, 0, 1];
// Handle context types.
if (!$context) {
$context = \context_system::instance();
}
switch ($context->contextlevel) {
case CONTEXT_MODULE:
case CONTEXT_BLOCK:
case CONTEXT_COURSE:
case CONTEXT_COURSECAT:
// These contexts cannot contain any users.
return null;
case CONTEXT_USER:
// Restrict to specific user.
$where .= ' AND id = ?';
$params[] = $context->instanceid;
break;
case CONTEXT_SYSTEM:
break;
default:
throw new \coding_exception('Unexpected contextlevel: ' . $context->contextlevel);
}
return $DB->get_recordset_select('user', $where, $params);
}
/**
* Returns document instances for each record in the recordset.
*
* @param \stdClass $record
* @param array $options
* @return \core_search\document
*/
public function get_document($record, $options = array()) {
$context = \context_system::instance();
// Prepare associative array with data from DB.
$doc = \core_search\document_factory::instance($record->id, $this->componentname, $this->areaname);
// Include all alternate names in title.
$array = [];
foreach (\core_user\fields::get_name_fields(true) as $field) {
$array[$field] = $record->$field;
}
$fullusername = join(' ', $array);
// Assigning properties to our document.
$doc->set('title', content_to_text($fullusername, false));
$doc->set('contextid', $context->id);
$doc->set('courseid', SITEID);
$doc->set('itemid', $record->id);
$doc->set('modified', $record->timemodified);
$doc->set('owneruserid', \core_search\manager::NO_OWNER_ID);
$doc->set('content', content_to_text($record->description, $record->descriptionformat));
// Check if this document should be considered new.
if (isset($options['lastindexedtime']) && $options['lastindexedtime'] < $record->timecreated) {
// If the document was created after the last index time, it must be new.
$doc->set_is_new(true);
}
return $doc;
}
/**
* Returns the user fullname to display as document title
*
* @param \core_search\document $doc
* @return string User fullname
*/
public function get_document_display_title(\core_search\document $doc) {
$user = \core_user::get_user($doc->get('itemid'));
return fullname($user);
}
/**
* Checking whether I can access a document
*
* @param int $id user id
* @return int
*/
public function check_access($id) {
global $DB, $USER;
$user = $DB->get_record('user', array('id' => $id));
if (!$user || $user->deleted) {
return \core_search\manager::ACCESS_DELETED;
}
if (user_can_view_profile($user)) {
return \core_search\manager::ACCESS_GRANTED;
}
return \core_search\manager::ACCESS_DENIED;
}
/**
* Returns a url to the profile page of user.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_doc_url(\core_search\document $doc) {
return $this->get_context_url($doc);
}
/**
* Returns a url to the document context.
*
* @param \core_search\document $doc
* @return \moodle_url
*/
public function get_context_url(\core_search\document $doc) {
return new \moodle_url('/user/profile.php', array('id' => $doc->get('itemid')));
}
/**
* Returns true if this area uses file indexing.
*
* @return bool
*/
public function uses_file_indexing() {
return true;
}
/**
* Return the context info required to index files for
* this search area.
*
* Should be onerridden by each search area.
*
* @return array
*/
public function get_search_fileareas() {
$fileareas = array(
'profile' // Fileareas.
);
return $fileareas;
}
/**
* Returns the moodle component name.
*
* It might be the plugin name (whole frankenstyle name) or the core subsystem name.
*
* @return string
*/
public function get_component_name() {
return 'user';
}
/**
* Returns an icon instance for the document.
*
* @param \core_search\document $doc
*
* @return \core_search\document_icon
*/
public function get_doc_icon(\core_search\document $doc): \core_search\document_icon {
return new \core_search\document_icon('i/user');
}
/**
* Returns a list of category names associated with the area.
*
* @return array
*/
public function get_category_names() {
return [\core_search\manager::SEARCH_AREA_CATEGORY_USERS];
}
}
+499
View File
@@ -0,0 +1,499 @@
<?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/>.
/**
* Contains the class used for the displaying the participants table.
*
* @package core_user
* @copyright 2017 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace core_user\table;
use DateTime;
use context;
use core_table\dynamic as dynamic_table;
use core_table\local\filter\filterset;
use core_user\output\status_field;
use core_user\table\participants_search;
use moodle_url;
defined('MOODLE_INTERNAL') || die;
global $CFG;
require_once($CFG->libdir . '/tablelib.php');
require_once($CFG->dirroot . '/user/lib.php');
/**
* Class for the displaying the participants table.
*
* @package core_user
* @copyright 2017 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class participants extends \table_sql implements dynamic_table {
/**
* @var int $courseid The course id
*/
protected $courseid;
/**
* @var string[] The list of countries.
*/
protected $countries;
/**
* @var \stdClass[] The list of groups with membership info for the course.
*/
protected $groups;
/**
* @var string[] Extra fields to display.
*/
protected $extrafields;
/**
* @var \stdClass $course The course details.
*/
protected $course;
/**
* @var context $context The course context.
*/
protected $context;
/**
* @var \stdClass[] List of roles indexed by roleid.
*/
protected $allroles;
/**
* @var \stdClass[] List of roles indexed by roleid.
*/
protected $allroleassignments;
/**
* @var \stdClass[] Assignable roles in this course.
*/
protected $assignableroles;
/**
* @var \stdClass[] Profile roles in this course.
*/
protected $profileroles;
/**
* @var filterset Filterset describing which participants to include.
*/
protected $filterset;
/** @var \stdClass[] $viewableroles */
private $viewableroles;
/** @var moodle_url $baseurl The base URL for the report. */
public $baseurl;
/**
* Render the participants table.
*
* @param int $pagesize Size of page for paginated displayed table.
* @param bool $useinitialsbar Whether to use the initials bar which will only be used if there is a fullname column defined.
* @param string $downloadhelpbutton
*/
public function out($pagesize, $useinitialsbar, $downloadhelpbutton = '') {
global $CFG, $OUTPUT, $PAGE;
// Define the headers and columns.
$headers = [];
$columns = [];
// At the very least, the user viewing this table will be able to use bulk actions to export it, so add 'select' column.
$mastercheckbox = new \core\output\checkbox_toggleall('participants-table', true, [
'id' => 'select-all-participants',
'name' => 'select-all-participants',
'label' => get_string('selectall'),
'labelclasses' => 'sr-only',
'classes' => 'm-1',
'checked' => false,
]);
$headers[] = $OUTPUT->render($mastercheckbox);
$columns[] = 'select';
$headers[] = get_string('fullname');
$columns[] = 'fullname';
$extrafields = \core_user\fields::get_identity_fields($this->context);
foreach ($extrafields as $field) {
$headers[] = \core_user\fields::get_display_name($field);
$columns[] = $field;
}
$headers[] = get_string('roles');
$columns[] = 'roles';
// Get the list of fields we have to hide.
$hiddenfields = array();
if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
}
// Add column for groups if the user can view them.
$canseegroups = !isset($hiddenfields['groups']);
if ($canseegroups) {
$headers[] = get_string('groups');
$columns[] = 'groups';
}
// Do not show the columns if it exists in the hiddenfields array.
if (!isset($hiddenfields['lastaccess'])) {
if ($this->courseid == SITEID) {
$headers[] = get_string('lastsiteaccess');
} else {
$headers[] = get_string('lastcourseaccess');
}
$columns[] = 'lastaccess';
}
$canreviewenrol = has_capability('moodle/course:enrolreview', $this->context);
if ($canreviewenrol && $this->courseid != SITEID) {
$columns[] = 'status';
$headers[] = get_string('participationstatus', 'enrol');
$this->no_sorting('status');
};
$this->define_columns($columns);
$this->define_headers($headers);
// The name column is a header.
$this->define_header_column('fullname');
// Make this table sorted by last name by default.
$this->sortable(true, 'lastname');
$this->no_sorting('select');
$this->no_sorting('roles');
if ($canseegroups) {
$this->no_sorting('groups');
}
$this->set_default_per_page(20);
$this->set_attribute('id', 'participants');
$this->countries = get_string_manager()->get_list_of_countries(true);
$this->extrafields = $extrafields;
if ($canseegroups) {
$this->groups = groups_get_all_groups($this->courseid, 0, 0, 'g.*', true);
}
// If user has capability to review enrol, show them both role names.
$allrolesnamedisplay = ($canreviewenrol ? ROLENAME_BOTH : ROLENAME_ALIAS);
$this->allroles = role_fix_names(get_all_roles($this->context), $this->context, $allrolesnamedisplay);
$this->assignableroles = get_assignable_roles($this->context, ROLENAME_BOTH, false);
$this->profileroles = get_profile_roles($this->context);
$this->viewableroles = get_viewable_roles($this->context);
parent::out($pagesize, $useinitialsbar, $downloadhelpbutton);
if (has_capability('moodle/course:enrolreview', $this->context)) {
$params = [
'contextid' => $this->context->id,
'uniqueid' => $this->uniqueid,
];
$PAGE->requires->js_call_amd('core_user/status_field', 'init', [$params]);
}
}
/**
* Generate the select column.
*
* @param \stdClass $data
* @return string
*/
public function col_select($data) {
global $OUTPUT;
$checkbox = new \core\output\checkbox_toggleall('participants-table', false, [
'classes' => 'usercheckbox m-1',
'id' => 'user' . $data->id,
'name' => 'user' . $data->id,
'checked' => false,
'label' => get_string('selectitem', 'moodle', fullname($data)),
'labelclasses' => 'accesshide',
]);
return $OUTPUT->render($checkbox);
}
/**
* Generate the fullname column.
*
* @param \stdClass $data
* @return string
*/
public function col_fullname($data) {
global $OUTPUT;
return $OUTPUT->render(\core_user::get_profile_picture($data, null,
['courseid' => $this->course->id, 'includefullname' => true]));
}
/**
* User roles column.
*
* @param \stdClass $data
* @return string
*/
public function col_roles($data) {
global $OUTPUT;
$roles = isset($this->allroleassignments[$data->id]) ? $this->allroleassignments[$data->id] : [];
$editable = new \core_user\output\user_roles_editable($this->course,
$this->context,
$data,
$this->allroles,
$this->assignableroles,
$this->profileroles,
$roles,
$this->viewableroles);
return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT));
}
/**
* Generate the groups column.
*
* @param \stdClass $data
* @return string
*/
public function col_groups($data) {
global $OUTPUT;
$usergroups = [];
foreach ($this->groups as $coursegroup) {
if (isset($coursegroup->members[$data->id])) {
$usergroups[] = $coursegroup->id;
}
}
$editable = new \core_group\output\user_groups_editable($this->course, $this->context, $data, $this->groups, $usergroups);
return $OUTPUT->render_from_template('core/inplace_editable', $editable->export_for_template($OUTPUT));
}
/**
* Generate the country column.
*
* @param \stdClass $data
* @return string
*/
public function col_country($data) {
if (!empty($this->countries[$data->country])) {
return $this->countries[$data->country];
}
return '';
}
/**
* Generate the last access column.
*
* @param \stdClass $data
* @return string
*/
public function col_lastaccess($data) {
if ($data->lastaccess) {
return format_time(time() - $data->lastaccess);
}
return get_string('never');
}
/**
* Generate the status column.
*
* @param \stdClass $data The data object.
* @return string
*/
public function col_status($data) {
global $CFG, $OUTPUT, $PAGE;
$enrolstatusoutput = '';
$canreviewenrol = has_capability('moodle/course:enrolreview', $this->context);
if ($canreviewenrol) {
$canviewfullnames = has_capability('moodle/site:viewfullnames', $this->context);
$fullname = htmlspecialchars(fullname($data, $canviewfullnames), ENT_QUOTES, 'utf-8');
$coursename = format_string($this->course->fullname, true, array('context' => $this->context));
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new \course_enrolment_manager($PAGE, $this->course);
$userenrolments = $manager->get_user_enrolments($data->id);
foreach ($userenrolments as $ue) {
$timestart = $ue->timestart;
$timeend = $ue->timeend;
$timeenrolled = $ue->timecreated;
$actions = $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue);
$instancename = $ue->enrolmentinstancename;
// Default status field label and value.
$status = get_string('participationactive', 'enrol');
$statusval = status_field::STATUS_ACTIVE;
switch ($ue->status) {
case ENROL_USER_ACTIVE:
$currentdate = new DateTime();
$now = $currentdate->getTimestamp();
$isexpired = $timestart > $now || ($timeend > 0 && $timeend < $now);
$enrolmentdisabled = $ue->enrolmentinstance->status == ENROL_INSTANCE_DISABLED;
// If user enrolment status has not yet started/already ended or the enrolment instance is disabled.
if ($isexpired || $enrolmentdisabled) {
$status = get_string('participationnotcurrent', 'enrol');
$statusval = status_field::STATUS_NOT_CURRENT;
}
break;
case ENROL_USER_SUSPENDED:
$status = get_string('participationsuspended', 'enrol');
$statusval = status_field::STATUS_SUSPENDED;
break;
}
$statusfield = new status_field($instancename, $coursename, $fullname, $status, $timestart, $timeend,
$actions, $timeenrolled);
$statusfielddata = $statusfield->set_status($statusval)->export_for_template($OUTPUT);
$enrolstatusoutput .= $OUTPUT->render_from_template('core_user/status_field', $statusfielddata);
}
}
return $enrolstatusoutput;
}
/**
* This function is used for the extra user fields.
*
* These are being dynamically added to the table so there are no functions 'col_<userfieldname>' as
* the list has the potential to increase in the future and we don't want to have to remember to add
* a new method to this class. We also don't want to pollute this class with unnecessary methods.
*
* @param string $colname The column name
* @param \stdClass $data
* @return string
*/
public function other_cols($colname, $data) {
// Do not process if it is not a part of the extra fields.
if (!in_array($colname, $this->extrafields)) {
return '';
}
return s($data->{$colname});
}
/**
* Query the database for results to display in the table.
*
* @param int $pagesize size of page for paginated displayed table.
* @param bool $useinitialsbar do you want to use the initials bar.
*/
public function query_db($pagesize, $useinitialsbar = true) {
list($twhere, $tparams) = $this->get_sql_where();
$psearch = new participants_search($this->course, $this->context, $this->filterset);
$total = $psearch->get_total_participants_count($twhere, $tparams);
$this->pagesize($pagesize, $total);
$sort = $this->get_sql_sort();
if ($sort) {
$sort = 'ORDER BY ' . $sort;
}
$rawdata = $psearch->get_participants($twhere, $tparams, $sort, $this->get_page_start(), $this->get_page_size());
$this->rawdata = [];
foreach ($rawdata as $user) {
$this->rawdata[$user->id] = $user;
}
$rawdata->close();
if ($this->rawdata) {
$this->allroleassignments = get_users_roles($this->context, array_keys($this->rawdata),
true, 'c.contextlevel DESC, r.sortorder ASC');
} else {
$this->allroleassignments = [];
}
// Set initial bars.
if ($useinitialsbar) {
$this->initialbars(true);
}
}
/**
* Override the table show_hide_link to not show for select column.
*
* @param string $column the column name, index into various names.
* @param int $index numerical index of the column.
* @return string HTML fragment.
*/
protected function show_hide_link($column, $index) {
if ($index > 0) {
return parent::show_hide_link($column, $index);
}
return '';
}
/**
* Set filters and build table structure.
*
* @param filterset $filterset The filterset object to get the filters from.
*/
public function set_filterset(filterset $filterset): void {
// Get the context.
$this->courseid = $filterset->get_filter('courseid')->current();
$this->course = get_course($this->courseid);
$this->context = \context_course::instance($this->courseid, MUST_EXIST);
// Process the filterset.
parent::set_filterset($filterset);
}
/**
* Guess the base url for the participants table.
*/
public function guess_base_url(): void {
$this->baseurl = new moodle_url('/user/index.php', ['id' => $this->courseid]);
}
/**
* Get the context of the current table.
*
* Note: This function should not be called until after the filterset has been provided.
*
* @return context
*/
public function get_context(): context {
return $this->context;
}
/**
* Check if the user has the capability to access this table.
*
* @return bool Return true if capability check passed.
*/
public function has_capability(): bool {
global $CFG;
require_once($CFG->dirroot . '/course/lib.php');
$context = $this->course->id == SITEID ? \context_system::instance() : $this->get_context();
return course_can_view_participants($context);
}
}
@@ -0,0 +1,80 @@
<?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/>.
/**
* Participants table filterset.
*
* @package core
* @category table
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace core_user\table;
use core_table\local\filter\filterset;
use core_table\local\filter\integer_filter;
use core_table\local\filter\string_filter;
/**
* Participants table filterset.
*
* @package core
* @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class participants_filterset extends filterset {
/**
* Get the required filters.
*
* The only required filter is the courseid filter.
*
* @return array.
*/
public function get_required_filters(): array {
return [
'courseid' => integer_filter::class,
];
}
/**
* Get the optional filters.
*
* These are:
* - accesssince;
* - enrolments;
* - groups;
* - keywords;
* - country;
* - roles; and
* - status.
*
* @return array
*/
public function get_optional_filters(): array {
return [
'accesssince' => integer_filter::class,
'enrolments' => integer_filter::class,
'groups' => integer_filter::class,
'keywords' => string_filter::class,
'country' => string_filter::class,
'roles' => integer_filter::class,
'status' => integer_filter::class,
];
}
}
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
<?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/>.
/**
* Contact site support.
*
* @copyright 2022 Simey Lameze <simey@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->dirroot . '/user/lib.php');
$user = isloggedin() && !isguestuser() ? $USER : null;
// If not allowed to view this page, redirect to the homepage. This would be where the site has
// disabled support, or limited it to authenticated users and the current user is a guest or not logged in.
if (!isset($CFG->supportavailability) ||
$CFG->supportavailability == CONTACT_SUPPORT_DISABLED ||
($CFG->supportavailability == CONTACT_SUPPORT_AUTHENTICATED && is_null($user))) {
redirect($CFG->wwwroot);
}
if (!empty($CFG->supportpage)) {
redirect($CFG->supportpage);
}
$PAGE->set_context(context_system::instance());
$PAGE->set_url('/user/contactsitesupport.php');
$PAGE->set_title(get_string('contactsitesupport', 'admin'));
$PAGE->set_heading(get_string('contactsitesupport', 'admin'));
$PAGE->set_pagelayout('standard');
$renderer = $PAGE->get_renderer('user');
$form = new \core_user\form\contactsitesupport_form(null, $user);
if ($form->is_cancelled()) {
redirect($CFG->wwwroot);
} else if ($form->is_submitted() && $form->is_validated() && confirm_sesskey()) {
$data = $form->get_data();
$from = $user ?? core_user::get_noreply_user();
$subject = get_string('supportemailsubject', 'admin', format_string($SITE->fullname));
$data->notloggedinuser = (!$user);
$message = $renderer->render_from_template('user/contact_site_support_email_body', $data);
if (!email_to_user(core_user::get_support_user(), $from, $subject, $message)) {
$supportemail = $CFG->supportemail;
$form->set_data($data);
$templatectx = [
'supportemail' => $user ? html_writer::link("mailto:{$supportemail}", $supportemail) : false,
'supportform' => $form->render(),
];
$output = $renderer->render_from_template('user/contact_site_support_not_available', $templatectx);
} else {
$level = \core\output\notification::NOTIFY_SUCCESS;
redirect($CFG->wwwroot, get_string('supportmessagesent', 'user'), 3, $level);
}
} else {
$output = $form->render();
}
echo $OUTPUT->header();
echo $output;
echo $OUTPUT->footer();
+71
View File
@@ -0,0 +1,71 @@
<?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/>.
/**
* Allows you to edit a users profile
*
* @copyright 2020 François Moreau
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/lib.php');
require_login();
$userid = optional_param('id', $USER->id, PARAM_INT); // User id.
$PAGE->set_url('/user/contentbank.php', ['id' => $userid]);
list($user, $course) = useredit_setup_preference_page($userid, SITEID);
$form = new \core_user\form\contentbank_user_preferences_form(null, ['userid' => $user->id]);
$user->contentvisibility = get_user_preferences('core_contentbank_visibility',
$CFG->defaultpreference_core_contentbank_visibility, $user->id);
$form->set_data($user);
$redirect = new moodle_url("/user/preferences.php", ['userid' => $user->id]);
if ($form->is_cancelled()) {
redirect($redirect);
} else if ($data = $form->get_data()) {
$data = $form->get_data();
$usernew = [
'id' => $user->id,
'preference_core_contentbank_visibility' => $data->contentvisibility
];
useredit_update_user_preference($usernew);
\core\event\user_updated::create_from_userid($user->id)->trigger();
redirect($redirect);
}
$title = get_string('contentbankpreferences', 'core_contentbank');
$userfullname = fullname($user, true);
$PAGE->navbar->includesettingsbase = true;
$PAGE->set_title("$course->shortname: $title");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($title);
$form->display();
echo $OUTPUT->footer();
+73
View File
@@ -0,0 +1,73 @@
<?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/>.
/**
* Allow user to set their default home page
*
* @package core_user
* @copyright 2019 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once('../config.php');
require_once($CFG->dirroot . '/user/editlib.php');
require_once($CFG->dirroot . '/user/lib.php');
$userid = optional_param('id', $USER->id, PARAM_INT);
$PAGE->set_url('/user/defaulthomepage.php', ['id' => $userid]);
list($user, $course) = useredit_setup_preference_page($userid, SITEID);
$form = new core_user\form\defaulthomepage_form();
$defaulthomepage = get_default_home_page();
$user->defaulthomepage = get_user_preferences('user_home_page_preference', $defaulthomepage, $user);
if (empty($CFG->enabledashboard) && $user->defaulthomepage == HOMEPAGE_MY) {
// If the user was using the dashboard but it's disabled, return the default home page.
$user->defaulthomepage = $defaulthomepage;
}
$form->set_data($user);
$redirect = new moodle_url('/user/preferences.php', ['userid' => $user->id]);
if ($form->is_cancelled()) {
redirect($redirect);
} else if ($data = $form->get_data()) {
$userupdate = [
'id' => $user->id,
'preference_user_home_page_preference' => $data->defaulthomepage,
];
useredit_update_user_preference($userupdate);
\core\event\user_updated::create_from_userid($userupdate['id'])->trigger();
redirect($redirect);
}
$PAGE->navbar->includesettingsbase = true;
$strdefaulthomepageuser = get_string('defaulthomepageuser');
$PAGE->set_title("$course->shortname: $strdefaulthomepageuser");
$PAGE->set_heading(fullname($user, true));
echo $OUTPUT->header();
echo $OUTPUT->heading($strdefaulthomepageuser);
$form->display();
echo $OUTPUT->footer();
+325
View File
@@ -0,0 +1,325 @@
<?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/>.
/**
* Allows you to edit a users profile
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->libdir.'/gdlib.php');
require_once($CFG->dirroot.'/user/edit_form.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
$userid = optional_param('id', $USER->id, PARAM_INT); // User id.
$course = optional_param('course', SITEID, PARAM_INT); // Course id (defaults to Site).
$returnto = optional_param('returnto', null, PARAM_ALPHA); // Code determining where to return to after save.
$cancelemailchange = optional_param('cancelemailchange', 0, PARAM_INT); // Course id (defaults to Site).
$PAGE->set_url('/user/edit.php', array('course' => $course, 'id' => $userid));
if (!$course = $DB->get_record('course', array('id' => $course))) {
throw new \moodle_exception('invalidcourseid');
}
if ($course->id != SITEID) {
require_login($course);
} else if (!isloggedin()) {
if (empty($SESSION->wantsurl)) {
$SESSION->wantsurl = $CFG->wwwroot.'/user/edit.php';
}
redirect(get_login_url());
} else {
$PAGE->set_context(context_system::instance());
}
// Guest can not edit.
if (isguestuser()) {
throw new \moodle_exception('guestnoeditprofile');
}
// The user profile we are editing.
if (!$user = $DB->get_record('user', array('id' => $userid))) {
throw new \moodle_exception('invaliduserid');
}
// Guest can not be edited.
if (isguestuser($user)) {
throw new \moodle_exception('guestnoeditprofile');
}
// User interests separated by commas.
$user->interests = core_tag_tag::get_item_tags_array('core', 'user', $user->id);
// Remote users cannot be edited. Note we have to perform the strict user_not_fully_set_up() check.
// Otherwise the remote user could end up in endless loop between user/view.php and here.
// Required custom fields are not supported in MNet environment anyway.
if (is_mnet_remote_user($user)) {
if (user_not_fully_set_up($user, true)) {
$hostwwwroot = $DB->get_field('mnet_host', 'wwwroot', array('id' => $user->mnethostid));
throw new \moodle_exception('usernotfullysetup', 'mnet', '', $hostwwwroot);
}
redirect($CFG->wwwroot . "/user/view.php?course={$course->id}");
}
// Load the appropriate auth plugin.
$userauth = get_auth_plugin($user->auth);
if (!$userauth->can_edit_profile()) {
throw new \moodle_exception('noprofileedit', 'auth');
}
if ($editurl = $userauth->edit_profile_url()) {
// This internal script not used.
redirect($editurl);
}
if ($course->id == SITEID) {
$coursecontext = context_system::instance(); // SYSTEM context.
} else {
$coursecontext = context_course::instance($course->id); // Course context.
}
$systemcontext = context_system::instance();
$personalcontext = context_user::instance($user->id);
// Check access control.
if ($user->id == $USER->id) {
// Editing own profile - require_login() MUST NOT be used here, it would result in infinite loop!
if (!has_capability('moodle/user:editownprofile', $systemcontext)) {
throw new \moodle_exception('cannotedityourprofile');
}
} else {
// Teachers, parents, etc.
require_capability('moodle/user:editprofile', $personalcontext);
// No editing of guest user account.
if (isguestuser($user->id)) {
throw new \moodle_exception('guestnoeditprofileother');
}
// No editing of primary admin!
if (is_siteadmin($user) and !is_siteadmin($USER)) { // Only admins may edit other admins.
throw new \moodle_exception('useradmineditadmin');
}
}
if ($user->deleted) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('userdeleted'));
echo $OUTPUT->footer();
die;
}
$PAGE->set_pagelayout('admin');
$PAGE->add_body_class('limitedwidth');
$PAGE->set_context($personalcontext);
if ($USER->id != $user->id) {
$PAGE->navigation->extend_for_user($user);
} else {
if ($node = $PAGE->navigation->find('myprofile', navigation_node::TYPE_ROOTNODE)) {
$node->force_open();
}
}
// Process email change cancellation.
if ($cancelemailchange) {
cancel_email_update($user->id);
}
// Load user preferences.
useredit_load_preferences($user);
// Load custom profile fields data.
profile_load_data($user);
// Prepare the editor and create form.
$editoroptions = array(
'maxfiles' => EDITOR_UNLIMITED_FILES,
'maxbytes' => $CFG->maxbytes,
'trusttext' => false,
'forcehttps' => false,
'context' => $personalcontext
);
$user = file_prepare_standard_editor($user, 'description', $editoroptions, $personalcontext, 'user', 'profile', 0);
// Prepare filemanager draft area.
$draftitemid = 0;
$filemanagercontext = $editoroptions['context'];
$filemanageroptions = array('maxbytes' => $CFG->maxbytes,
'subdirs' => 0,
'maxfiles' => 1,
'accepted_types' => 'optimised_image');
file_prepare_draft_area($draftitemid, $filemanagercontext->id, 'user', 'newicon', 0, $filemanageroptions);
$user->imagefile = $draftitemid;
// Create form.
$userform = new user_edit_form(new moodle_url($PAGE->url, array('returnto' => $returnto)), array(
'editoroptions' => $editoroptions,
'filemanageroptions' => $filemanageroptions,
'user' => $user));
$emailchanged = false;
// Deciding where to send the user back in most cases.
if ($returnto === 'profile') {
if ($course->id != SITEID) {
$returnurl = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id));
} else {
$returnurl = new moodle_url('/user/profile.php', array('id' => $user->id));
}
} else {
$returnurl = new moodle_url('/user/preferences.php', array('userid' => $user->id));
}
if ($userform->is_cancelled()) {
redirect($returnurl);
} else if ($usernew = $userform->get_data()) {
$emailchangedhtml = '';
if ($CFG->emailchangeconfirmation) {
// Users with 'moodle/user:update' can change their email address immediately.
// Other users require a confirmation email.
if (isset($usernew->email) and $user->email != $usernew->email && !has_capability('moodle/user:update', $systemcontext)) {
$a = new stdClass();
$emailchangedkey = random_string(20);
set_user_preference('newemail', $usernew->email, $user->id);
set_user_preference('newemailkey', $emailchangedkey, $user->id);
set_user_preference('newemailattemptsleft', 3, $user->id);
$a->newemail = $emailchanged = $usernew->email;
$a->oldemail = $usernew->email = $user->email;
$emailchangedhtml = $OUTPUT->box(get_string('auth_changingemailaddress', 'auth', $a), 'generalbox', 'notice');
$emailchangedhtml .= $OUTPUT->continue_button($returnurl);
}
}
$authplugin = get_auth_plugin($user->auth);
$usernew->timemodified = time();
// Description editor element may not exist!
if (isset($usernew->description_editor) && isset($usernew->description_editor['format'])) {
$usernew = file_postupdate_standard_editor($usernew, 'description', $editoroptions, $personalcontext, 'user', 'profile', 0);
}
// Pass a true old $user here.
if (!$authplugin->user_update($user, $usernew)) {
// Auth update failed.
throw new \moodle_exception('cannotupdateprofile');
}
// Update user with new profile data.
user_update_user($usernew, false, false);
// Update preferences.
useredit_update_user_preference($usernew);
// Update interests.
if (isset($usernew->interests)) {
useredit_update_interests($usernew, $usernew->interests);
}
// Update user picture.
if (empty($CFG->disableuserimages)) {
core_user::update_picture($usernew, $filemanageroptions);
}
// Update mail bounces.
useredit_update_bounces($user, $usernew);
// Update forum track preference.
useredit_update_trackforums($user, $usernew);
// Save custom profile fields data.
profile_save_data($usernew);
// Trigger event.
\core\event\user_updated::create_from_userid($user->id)->trigger();
// If email was changed and confirmation is required, send confirmation email now to the new address.
if ($emailchanged !== false && $CFG->emailchangeconfirmation) {
$tempuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
$tempuser->email = $emailchanged;
$a = new stdClass();
$a->url = $CFG->wwwroot . '/user/emailupdate.php?key=' . $emailchangedkey . '&id=' . $user->id;
$a->site = format_string($SITE->fullname, true, array('context' => context_course::instance(SITEID)));
$a->fullname = fullname($tempuser, true);
$a->supportemail = $OUTPUT->supportemail();
$emailupdatemessage = get_string('emailupdatemessage', 'auth', $a);
$emailupdatetitle = get_string('emailupdatetitle', 'auth', $a);
// Email confirmation directly rather than using messaging so they will definitely get an email.
$noreplyuser = core_user::get_noreply_user();
if (!$mailresults = email_to_user($tempuser, $noreplyuser, $emailupdatetitle, $emailupdatemessage)) {
die("could not send email!");
}
}
// Reload from db, we need new full name on this page if we do not redirect.
$user = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
if ($USER->id == $user->id) {
// Override old $USER session variable if needed.
foreach ((array)$user as $variable => $value) {
if ($variable === 'description' or $variable === 'password') {
// These are not set for security nad perf reasons.
continue;
}
$USER->$variable = $value;
}
// Preload custom fields.
profile_load_custom_fields($USER);
}
if (is_siteadmin() and empty($SITE->shortname)) {
// Fresh cli install - we need to finish site settings.
redirect(new moodle_url('/admin/index.php'));
}
if (!$emailchanged || !$CFG->emailchangeconfirmation) {
redirect($returnurl, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
}
// Display page header.
$streditmyprofile = get_string('editmyprofile');
$strparticipants = get_string('participants');
$userfullname = fullname($user, true);
$PAGE->set_title("$course->shortname: $streditmyprofile");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($userfullname);
if ($emailchanged) {
echo $emailchangedhtml;
} else {
// Finally display THE form.
$userform->display();
}
// And proper footer.
echo $OUTPUT->footer();
+245
View File
@@ -0,0 +1,245 @@
<?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/>.
/**
* Form to edit a users profile
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Class user_edit_form.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_edit_form extends moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG, $COURSE, $USER;
$mform = $this->_form;
$editoroptions = null;
$filemanageroptions = null;
$usernotfullysetup = user_not_fully_set_up($USER);
if (!is_array($this->_customdata)) {
throw new coding_exception('invalid custom data for user_edit_form');
}
$editoroptions = $this->_customdata['editoroptions'];
$filemanageroptions = $this->_customdata['filemanageroptions'];
$user = $this->_customdata['user'];
$userid = $user->id;
if (empty($user->country)) {
// We must unset the value here so $CFG->country can be used as default one.
unset($user->country);
}
// Accessibility: "Required" is bad legend text.
$strgeneral = get_string('general');
$strrequired = get_string('required');
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'course', $COURSE->id);
$mform->setType('course', PARAM_INT);
// Print the required moodle fields first.
$mform->addElement('header', 'moodle', $strgeneral);
// Shared fields.
useredit_shared_definition($mform, $editoroptions, $filemanageroptions, $user);
// Extra settigs.
if (!empty($CFG->disableuserimages) || $usernotfullysetup) {
$mform->removeElement('deletepicture');
$mform->removeElement('imagefile');
$mform->removeElement('imagealt');
}
// If the user isn't fully set up, let them know that they will be able to change
// their profile picture once their profile is complete.
if ($usernotfullysetup) {
$userpicturewarning = $mform->createElement('warning', 'userpicturewarning', 'notifymessage', get_string('newpictureusernotsetup'));
$enabledusernamefields = useredit_get_enabled_name_fields();
if ($mform->elementExists('moodle_additional_names')) {
$mform->insertElementBefore($userpicturewarning, 'moodle_additional_names');
} else if ($mform->elementExists('moodle_interests')) {
$mform->insertElementBefore($userpicturewarning, 'moodle_interests');
} else {
$mform->insertElementBefore($userpicturewarning, 'moodle_optional');
}
// This is expected to exist when the form is submitted.
$imagefile = $mform->createElement('hidden', 'imagefile');
$mform->insertElementBefore($imagefile, 'userpicturewarning');
}
// Next the customisable profile fields.
profile_definition($mform, $userid);
$this->add_action_buttons(true, get_string('updatemyprofile'));
$this->set_data($user);
}
/**
* Extend the form definition after the data has been parsed.
*/
public function definition_after_data() {
global $CFG, $DB, $OUTPUT;
$mform = $this->_form;
$userid = $mform->getElementValue('id');
// Trim required name fields.
foreach (useredit_get_required_name_fields() as $field) {
$mform->applyFilter($field, 'trim');
}
if ($user = $DB->get_record('user', array('id' => $userid))) {
// Remove description.
if (empty($user->description) && !empty($CFG->profilesforenrolledusersonly) && !$DB->record_exists('role_assignments', array('userid' => $userid))) {
$mform->removeElement('description_editor');
}
// Print picture.
$context = context_user::instance($user->id, MUST_EXIST);
$fs = get_file_storage();
$hasuploadedpicture = ($fs->file_exists($context->id, 'user', 'icon', 0, '/', 'f2.png') || $fs->file_exists($context->id, 'user', 'icon', 0, '/', 'f2.jpg'));
if (!empty($user->picture) && $hasuploadedpicture) {
$imagevalue = $OUTPUT->user_picture($user, array('courseid' => SITEID, 'size' => 64));
} else {
$imagevalue = get_string('none');
}
$imageelement = $mform->getElement('currentpicture');
$imageelement->setValue($imagevalue);
if ($mform->elementExists('deletepicture') && !$hasuploadedpicture) {
$mform->removeElement('deletepicture');
}
// Disable fields that are locked by auth plugins.
$fields = get_user_fieldnames();
$authplugin = get_auth_plugin($user->auth);
$customfields = $authplugin->get_custom_user_profile_fields();
$customfieldsdata = profile_user_record($userid, false);
$fields = array_merge($fields, $customfields);
foreach ($fields as $field) {
if ($field === 'description') {
// Hard coded hack for description field. See MDL-37704 for details.
$formfield = 'description_editor';
} else {
$formfield = $field;
}
if (!$mform->elementExists($formfield)) {
continue;
}
// Get the original value for the field.
if (in_array($field, $customfields)) {
$key = str_replace('profile_field_', '', $field);
$value = isset($customfieldsdata->{$key}) ? $customfieldsdata->{$key} : '';
} else {
$value = $user->{$field};
}
$configvariable = 'field_lock_' . $field;
if (isset($authplugin->config->{$configvariable})) {
if ($authplugin->config->{$configvariable} === 'locked') {
$mform->hardFreeze($formfield);
$mform->setConstant($formfield, $value);
} else if ($authplugin->config->{$configvariable} === 'unlockedifempty' and $value != '') {
$mform->hardFreeze($formfield);
$mform->setConstant($formfield, $value);
}
}
}
// Next the customisable profile fields.
profile_definition_after_data($mform, $user->id);
} else {
profile_definition_after_data($mform, 0);
}
}
/**
* Validate incoming form data.
* @param array $usernew
* @param array $files
* @return array
*/
public function validation($usernew, $files) {
global $CFG, $DB;
$errors = parent::validation($usernew, $files);
$usernew = (object)$usernew;
$user = $DB->get_record('user', array('id' => $usernew->id));
// Validate email.
if (!isset($usernew->email)) {
// Mail not confirmed yet.
} else if (!validate_email($usernew->email)) {
$errors['email'] = get_string('invalidemail');
} else if (($usernew->email !== $user->email) && empty($CFG->allowaccountssameemail)) {
// Make a case-insensitive query for the given email address.
$select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid AND id <> :userid';
$params = array(
'email' => $usernew->email,
'mnethostid' => $CFG->mnet_localhost_id,
'userid' => $usernew->id
);
// If there are other user(s) that already have the same email, show an error.
if ($DB->record_exists_select('user', $select, $params)) {
$errors['email'] = get_string('emailexists');
}
}
if (isset($usernew->email) and $usernew->email === $user->email and over_bounce_threshold($user)) {
$errors['email'] = get_string('toomanybounces');
}
if (isset($usernew->email) and !empty($CFG->verifychangedemail) and !isset($errors['email']) and !has_capability('moodle/user:update', context_system::instance())) {
$errorstr = email_is_not_allowed($usernew->email);
if ($errorstr !== false) {
$errors['email'] = $errorstr;
}
}
// Next the customisable profile fields.
$errors += profile_validation($usernew, $files);
return $errors;
}
}
+369
View File
@@ -0,0 +1,369 @@
<?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/>.
/**
* Allows you to edit a users profile
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->libdir.'/gdlib.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/user/editadvanced_form.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/profile/lib.php');
require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->dirroot.'/webservice/lib.php');
$id = optional_param('id', $USER->id, PARAM_INT); // User id; -1 if creating new user.
$course = optional_param('course', SITEID, PARAM_INT); // Course id (defaults to Site).
$returnto = optional_param('returnto', null, PARAM_ALPHA); // Code determining where to return to after save.
$PAGE->set_url('/user/editadvanced.php', array('course' => $course, 'id' => $id));
$course = $DB->get_record('course', array('id' => $course), '*', MUST_EXIST);
if (!empty($USER->newadminuser)) {
// Ignore double clicks, we must finish all operations before cancelling request.
ignore_user_abort(true);
$PAGE->set_course($SITE);
$PAGE->set_pagelayout('maintenance');
} else {
if ($course->id == SITEID) {
require_login();
$PAGE->set_context(context_system::instance());
} else {
require_login($course);
}
$PAGE->set_pagelayout('admin');
$PAGE->add_body_class('limitedwidth');
}
if ($course->id == SITEID) {
$coursecontext = context_system::instance(); // SYSTEM context.
} else {
$coursecontext = context_course::instance($course->id); // Course context.
}
$systemcontext = context_system::instance();
if ($id == -1) {
// Creating new user.
$user = new stdClass();
$user->id = -1;
$user->auth = 'manual';
$user->confirmed = 1;
$user->deleted = 0;
$user->timezone = '99';
require_capability('moodle/user:create', $systemcontext);
admin_externalpage_setup('addnewuser', '', array('id' => -1));
$PAGE->set_primary_active_tab('siteadminnode');
$PAGE->navbar->add(get_string('addnewuser', 'moodle'), $PAGE->url);
} else {
// Editing existing user.
require_capability('moodle/user:update', $systemcontext);
$user = $DB->get_record('user', array('id' => $id), '*', MUST_EXIST);
$PAGE->set_context(context_user::instance($user->id));
$PAGE->navbar->includesettingsbase = true;
if ($user->id != $USER->id) {
$PAGE->navigation->extend_for_user($user);
} else {
if ($node = $PAGE->navigation->find('myprofile', navigation_node::TYPE_ROOTNODE)) {
$node->force_open();
}
}
}
// Remote users cannot be edited.
if ($user->id != -1 and is_mnet_remote_user($user)) {
redirect($CFG->wwwroot . "/user/view.php?id=$id&course={$course->id}");
}
if ($user->id != $USER->id and is_siteadmin($user) and !is_siteadmin($USER)) { // Only admins may edit other admins.
throw new \moodle_exception('useradmineditadmin');
}
if (isguestuser($user->id)) { // The real guest user can not be edited.
throw new \moodle_exception('guestnoeditprofileother');
}
if ($user->deleted) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('userdeleted'));
echo $OUTPUT->footer();
die;
}
// Load user preferences.
useredit_load_preferences($user);
// Load custom profile fields data.
profile_load_data($user);
// User interests.
$user->interests = core_tag_tag::get_item_tags_array('core', 'user', $id);
if ($user->id !== -1) {
$usercontext = context_user::instance($user->id);
$editoroptions = array(
'maxfiles' => EDITOR_UNLIMITED_FILES,
'maxbytes' => $CFG->maxbytes,
'trusttext' => false,
'forcehttps' => false,
'context' => $usercontext
);
$user = file_prepare_standard_editor($user, 'description', $editoroptions, $usercontext, 'user', 'profile', 0);
} else {
$usercontext = null;
// This is a new user, we don't want to add files here.
$editoroptions = array(
'maxfiles' => 0,
'maxbytes' => 0,
'trusttext' => false,
'forcehttps' => false,
'context' => $coursecontext
);
}
// Prepare filemanager draft area.
$draftitemid = 0;
$filemanagercontext = $editoroptions['context'];
$filemanageroptions = array('maxbytes' => $CFG->maxbytes,
'subdirs' => 0,
'maxfiles' => 1,
'accepted_types' => 'optimised_image');
file_prepare_draft_area($draftitemid, $filemanagercontext->id, 'user', 'newicon', 0, $filemanageroptions);
$user->imagefile = $draftitemid;
// Create form.
$userform = new user_editadvanced_form(new moodle_url($PAGE->url, array('returnto' => $returnto)), array(
'editoroptions' => $editoroptions,
'filemanageroptions' => $filemanageroptions,
'user' => $user));
// Deciding where to send the user back in most cases.
if ($returnto === 'profile') {
if ($course->id != SITEID) {
$returnurl = new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $course->id));
} else {
$returnurl = new moodle_url('/user/profile.php', array('id' => $user->id));
}
} else if ($user->id === -1) {
$returnurl = new moodle_url("/admin/user.php");
} else {
$returnurl = new moodle_url('/user/preferences.php', array('userid' => $user->id));
}
if ($userform->is_cancelled()) {
redirect($returnurl);
} else if ($usernew = $userform->get_data()) {
$usercreated = false;
if (empty($usernew->auth)) {
// User editing self.
$authplugin = get_auth_plugin($user->auth);
unset($usernew->auth); // Can not change/remove.
} else {
$authplugin = get_auth_plugin($usernew->auth);
}
$usernew->timemodified = time();
$createpassword = false;
if ($usernew->id == -1) {
unset($usernew->id);
$createpassword = !empty($usernew->createpassword);
unset($usernew->createpassword);
$usernew = file_postupdate_standard_editor($usernew, 'description', $editoroptions, null, 'user', 'profile', null);
$usernew->mnethostid = $CFG->mnet_localhost_id; // Always local user.
$usernew->confirmed = 1;
$usernew->timecreated = time();
if ($authplugin->is_internal()) {
if ($createpassword or empty($usernew->newpassword)) {
$usernew->password = '';
} else {
$usernew->password = hash_internal_user_password($usernew->newpassword);
}
} else {
$usernew->password = AUTH_PASSWORD_NOT_CACHED;
}
$usernew->id = user_create_user($usernew, false, false);
if (!$authplugin->is_internal() and $authplugin->can_change_password() and !empty($usernew->newpassword)) {
if (!$authplugin->user_update_password($usernew, $usernew->newpassword)) {
// Do not stop here, we need to finish user creation.
debugging(get_string('cannotupdatepasswordonextauth', 'error', $usernew->auth), DEBUG_NONE);
}
}
$usercreated = true;
} else {
$usernew = file_postupdate_standard_editor($usernew, 'description', $editoroptions, $usercontext, 'user', 'profile', 0);
// Pass a true old $user here.
if (!$authplugin->user_update($user, $usernew)) {
// Auth update failed.
throw new \moodle_exception('cannotupdateuseronexauth', '', '', $user->auth);
}
user_update_user($usernew, false, false);
// Set new password if specified.
if (!empty($usernew->newpassword)) {
if ($authplugin->can_change_password()) {
if (!$authplugin->user_update_password($usernew, $usernew->newpassword)) {
throw new \moodle_exception('cannotupdatepasswordonextauth', '', '', $usernew->auth);
}
unset_user_preference('create_password', $usernew); // Prevent cron from generating the password.
if (!empty($CFG->passwordchangelogout)) {
// We can use SID of other user safely here because they are unique,
// the problem here is we do not want to logout admin here when changing own password.
\core\session\manager::kill_user_sessions($usernew->id, session_id());
}
if (!empty($usernew->signoutofotherservices)) {
webservice::delete_user_ws_tokens($usernew->id);
}
}
}
// Force logout if user just suspended.
if (isset($usernew->suspended) and $usernew->suspended and !$user->suspended) {
\core\session\manager::kill_user_sessions($user->id);
}
}
$usercontext = context_user::instance($usernew->id);
// Update preferences.
useredit_update_user_preference($usernew);
// Update tags.
if (empty($USER->newadminuser) && isset($usernew->interests)) {
useredit_update_interests($usernew, $usernew->interests);
}
// Update user picture.
if (empty($USER->newadminuser)) {
core_user::update_picture($usernew, $filemanageroptions);
}
// Update mail bounces.
useredit_update_bounces($user, $usernew);
// Update forum track preference.
useredit_update_trackforums($user, $usernew);
// Save custom profile fields data.
profile_save_data($usernew);
// Reload from db.
$usernew = $DB->get_record('user', array('id' => $usernew->id));
if ($createpassword) {
setnew_password_and_mail($usernew);
unset_user_preference('create_password', $usernew);
set_user_preference('auth_forcepasswordchange', 1, $usernew);
}
// Trigger update/create event, after all fields are stored.
if ($usercreated) {
\core\event\user_created::create_from_userid($usernew->id)->trigger();
} else {
\core\event\user_updated::create_from_userid($usernew->id)->trigger();
}
if ($user->id == $USER->id) {
// Override old $USER session variable.
foreach ((array)$usernew as $variable => $value) {
if ($variable === 'description' or $variable === 'password') {
// These are not set for security nad perf reasons.
continue;
}
$USER->$variable = $value;
}
// Preload custom fields.
profile_load_custom_fields($USER);
if (!empty($USER->newadminuser)) {
unset($USER->newadminuser);
// Apply defaults again - some of them might depend on admin user info, backup, roles, etc.
admin_apply_default_settings(null, false);
// Admin account is fully configured - set flag here in case the redirect does not work.
unset_config('adminsetuppending');
// Redirect to admin/ to continue with installation.
redirect("$CFG->wwwroot/$CFG->admin/");
} else if (empty($SITE->fullname)) {
// Somebody double clicked when editing admin user during install.
redirect("$CFG->wwwroot/$CFG->admin/");
} else {
redirect($returnurl, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
} else if ($returnto === 'profile') {
\core\session\manager::gc(); // Remove stale sessions.
redirect($returnurl, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
} else {
\core\session\manager::gc(); // Remove stale sessions.
redirect("$CFG->wwwroot/$CFG->admin/user.php", get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
// Never reached..
}
// Display page header.
if ($user->id == -1 or ($user->id != $USER->id)) {
if ($user->id == -1) {
echo $OUTPUT->header();
} else {
$streditmyprofile = get_string('editmyprofile');
$userfullname = fullname($user, true);
$PAGE->set_heading($userfullname);
$coursename = $course->id !== SITEID ? "$course->shortname" : '';
$PAGE->set_title("$streditmyprofile: $userfullname" . moodle_page::TITLE_SEPARATOR . $coursename);
echo $OUTPUT->header();
echo $OUTPUT->heading($userfullname);
}
} else if (!empty($USER->newadminuser)) {
$strinstallation = get_string('installation', 'install');
$strprimaryadminsetup = get_string('primaryadminsetup');
$PAGE->navbar->add($strprimaryadminsetup);
$PAGE->set_title($strinstallation);
$PAGE->set_heading($strinstallation);
$PAGE->set_cacheable(false);
echo $OUTPUT->header();
echo $OUTPUT->box(get_string('configintroadmin', 'admin'), 'generalbox boxwidthnormal boxaligncenter');
echo '<br />';
} else {
$streditmyprofile = get_string('editmyprofile');
$strparticipants = get_string('participants');
$strnewuser = get_string('newuser');
$userfullname = fullname($user, true);
$PAGE->set_title("$course->shortname: $streditmyprofile");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($streditmyprofile);
}
// Finally display THE form.
$userform->display();
// And proper footer.
echo $OUTPUT->footer();
+338
View File
@@ -0,0 +1,338 @@
<?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/>.
/**
* Form for editing a users profile
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
require_once($CFG->dirroot.'/user/lib.php');
/**
* Class user_editadvanced_form.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_editadvanced_form extends moodleform {
/**
* Define the form.
*/
public function definition() {
global $USER, $CFG, $COURSE;
$mform = $this->_form;
$editoroptions = null;
$filemanageroptions = null;
if (!is_array($this->_customdata)) {
throw new coding_exception('invalid custom data for user_edit_form');
}
$editoroptions = $this->_customdata['editoroptions'];
$filemanageroptions = $this->_customdata['filemanageroptions'];
$user = $this->_customdata['user'];
$userid = $user->id;
// Accessibility: "Required" is bad legend text.
$strgeneral = get_string('general');
$strrequired = get_string('required');
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', core_user::get_property_type('id'));
$mform->addElement('hidden', 'course', $COURSE->id);
$mform->setType('course', PARAM_INT);
// Print the required moodle fields first.
$mform->addElement('header', 'moodle', $strgeneral);
$auths = core_component::get_plugin_list('auth');
$enabled = get_string('pluginenabled', 'core_plugin');
$disabled = get_string('plugindisabled', 'core_plugin');
$authoptions = array($enabled => array(), $disabled => array());
$cannotchangepass = array();
$cannotchangeusername = array();
foreach ($auths as $auth => $unused) {
$authinst = get_auth_plugin($auth);
if (!$authinst->is_internal()) {
$cannotchangeusername[] = $auth;
}
$passwordurl = $authinst->change_password_url();
if (!($authinst->can_change_password() && empty($passwordurl))) {
if ($userid < 1 and $authinst->is_internal()) {
// This is unlikely but we can not create account without password
// when plugin uses passwords, we need to set it initially at least.
} else {
$cannotchangepass[] = $auth;
}
}
if (is_enabled_auth($auth)) {
$authoptions[$enabled][$auth] = get_string('pluginname', "auth_{$auth}");
} else {
$authoptions[$disabled][$auth] = get_string('pluginname', "auth_{$auth}");
}
}
$purpose = user_edit_map_field_purpose($userid, 'username');
$mform->addElement('text', 'username', get_string('username'), 'size="20"' . $purpose);
$mform->addHelpButton('username', 'username', 'auth');
$mform->setType('username', PARAM_RAW);
if ($userid !== -1) {
$mform->disabledIf('username', 'auth', 'in', $cannotchangeusername);
}
$mform->addElement('selectgroups', 'auth', get_string('chooseauthmethod', 'auth'), $authoptions);
$mform->addHelpButton('auth', 'chooseauthmethod', 'auth');
$mform->addElement('advcheckbox', 'suspended', get_string('suspended', 'auth'));
$mform->addHelpButton('suspended', 'suspended', 'auth');
$mform->addElement('checkbox', 'createpassword', get_string('createpassword', 'auth'));
$mform->disabledIf('createpassword', 'auth', 'in', $cannotchangepass);
if (!empty($CFG->passwordpolicy)) {
$mform->addElement('static', 'passwordpolicyinfo', '', print_password_policy());
}
$purpose = user_edit_map_field_purpose($userid, 'password');
$mform->addElement('passwordunmask', 'newpassword', get_string('newpassword'),
'maxlength="'.MAX_PASSWORD_CHARACTERS.'" size="20"' . $purpose);
$mform->addRule('newpassword', get_string('maximumchars', '', MAX_PASSWORD_CHARACTERS),
'maxlength', MAX_PASSWORD_CHARACTERS, 'client');
$mform->addHelpButton('newpassword', 'newpassword');
$mform->setType('newpassword', core_user::get_property_type('password'));
$mform->disabledIf('newpassword', 'createpassword', 'checked');
$mform->disabledIf('newpassword', 'auth', 'in', $cannotchangepass);
// Check if the user has active external tokens.
if ($userid and empty($CFG->passwordchangetokendeletion)) {
if ($tokens = webservice::get_active_tokens($userid)) {
$services = '';
foreach ($tokens as $token) {
$services .= format_string($token->servicename) . ',';
}
$services = get_string('userservices', 'webservice', rtrim($services, ','));
$mform->addElement('advcheckbox', 'signoutofotherservices', get_string('signoutofotherservices'), $services);
$mform->addHelpButton('signoutofotherservices', 'signoutofotherservices');
$mform->disabledIf('signoutofotherservices', 'newpassword', 'eq', '');
$mform->setDefault('signoutofotherservices', 1);
}
}
$mform->addElement('advcheckbox', 'preference_auth_forcepasswordchange', get_string('forcepasswordchange'));
$mform->addHelpButton('preference_auth_forcepasswordchange', 'forcepasswordchange');
$mform->disabledIf('preference_auth_forcepasswordchange', 'createpassword', 'checked');
// Shared fields.
useredit_shared_definition($mform, $editoroptions, $filemanageroptions, $user);
// Next the customisable profile fields.
profile_definition($mform, $userid);
if ($userid == -1) {
$btnstring = get_string('createuser');
} else {
$btnstring = get_string('updatemyprofile');
}
$this->add_action_buttons(true, $btnstring);
$this->set_data($user);
}
/**
* Extend the form definition after data has been parsed.
*/
public function definition_after_data() {
global $USER, $CFG, $DB, $OUTPUT;
$mform = $this->_form;
// Trim required name fields.
foreach (useredit_get_required_name_fields() as $field) {
$mform->applyFilter($field, 'trim');
}
if ($userid = $mform->getElementValue('id')) {
$user = $DB->get_record('user', array('id' => $userid));
} else {
$user = false;
}
// User can not change own auth method.
if ($userid == $USER->id) {
$mform->hardFreeze('auth');
$mform->hardFreeze('preference_auth_forcepasswordchange');
}
// Admin must choose some password and supply correct email.
if (!empty($USER->newadminuser)) {
$mform->addRule('newpassword', get_string('required'), 'required', null, 'client');
if ($mform->elementExists('suspended')) {
$mform->removeElement('suspended');
}
}
// Require password for new users.
if ($userid > 0) {
if ($mform->elementExists('createpassword')) {
$mform->removeElement('createpassword');
}
}
if ($user and is_mnet_remote_user($user)) {
// Only local accounts can be suspended.
if ($mform->elementExists('suspended')) {
$mform->removeElement('suspended');
}
}
if ($user and ($user->id == $USER->id or is_siteadmin($user))) {
// Prevent self and admin mess ups.
if ($mform->elementExists('suspended')) {
$mform->hardFreeze('suspended');
}
}
// Print picture.
if (empty($USER->newadminuser)) {
if ($user) {
$context = context_user::instance($user->id, MUST_EXIST);
$fs = get_file_storage();
$hasuploadedpicture = ($fs->file_exists($context->id, 'user', 'icon', 0, '/', 'f2.png') || $fs->file_exists($context->id, 'user', 'icon', 0, '/', 'f2.jpg'));
if (!empty($user->picture) && $hasuploadedpicture) {
$imagevalue = $OUTPUT->user_picture($user, array('courseid' => SITEID, 'size' => 64));
} else {
$imagevalue = get_string('none');
}
} else {
$imagevalue = get_string('none');
}
$imageelement = $mform->getElement('currentpicture');
$imageelement->setValue($imagevalue);
if ($user && $mform->elementExists('deletepicture') && !$hasuploadedpicture) {
$mform->removeElement('deletepicture');
}
}
// User changing their preferred theme will delete the cache for this theme.
if ($mform->elementExists('theme') && $mform->isSubmitted()) {
$theme = $mform->getSubmitValue('theme');
if (!empty($user) && ($theme != $user->theme)) {
theme_delete_used_in_context_cache($theme, $user->theme);
}
}
// Next the customisable profile fields.
profile_definition_after_data($mform, $userid);
}
/**
* Validate the form data.
* @param array $usernew
* @param array $files
* @return array|bool
*/
public function validation($usernew, $files) {
global $CFG, $DB;
$usernew = (object)$usernew;
$usernew->username = trim($usernew->username);
$user = $DB->get_record('user', array('id' => $usernew->id));
$err = array();
if (!$user and !empty($usernew->createpassword)) {
if ($usernew->suspended) {
// Show some error because we can not mail suspended users.
$err['suspended'] = get_string('error');
}
} else {
if (!empty($usernew->newpassword)) {
$errmsg = ''; // Prevent eclipse warning.
if (!check_password_policy($usernew->newpassword, $errmsg, $usernew)) {
$err['newpassword'] = $errmsg;
}
} else if (!$user) {
$auth = get_auth_plugin($usernew->auth);
if ($auth->is_internal()) {
// Internal accounts require password!
$err['newpassword'] = get_string('required');
}
}
}
if (empty($usernew->username)) {
// Might be only whitespace.
$err['username'] = get_string('required');
} else if (!$user or $user->username !== $usernew->username) {
// Check new username does not exist.
if ($DB->record_exists('user', array('username' => $usernew->username, 'mnethostid' => $CFG->mnet_localhost_id))) {
$err['username'] = get_string('usernameexists');
}
// Check allowed characters.
if ($usernew->username !== core_text::strtolower($usernew->username)) {
$err['username'] = get_string('usernamelowercase');
} else {
if ($usernew->username !== core_user::clean_field($usernew->username, 'username')) {
$err['username'] = get_string('invalidusername');
}
}
}
if (!$user or (isset($usernew->email) && $user->email !== $usernew->email)) {
if (!validate_email($usernew->email)) {
$err['email'] = get_string('invalidemail');
} else if (empty($CFG->allowaccountssameemail)) {
// Make a case-insensitive query for the given email address.
$select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid AND id <> :userid';
$params = array(
'email' => $usernew->email,
'mnethostid' => $CFG->mnet_localhost_id,
'userid' => $usernew->id
);
// If there are other user(s) that already have the same email, show an error.
if ($DB->record_exists_select('user', $select, $params)) {
$err['email'] = get_string('emailexists');
}
}
}
// Next the customisable profile fields.
$err += profile_validation($usernew, $files);
if (count($err) == 0) {
return true;
} else {
return $err;
}
}
}
+500
View File
@@ -0,0 +1,500 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains function used when editing a users profile and preferences.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once($CFG->dirroot . '/user/lib.php');
/**
* Cancels the requirement for a user to update their email address.
*
* @param int $userid
*/
function cancel_email_update($userid) {
unset_user_preference('newemail', $userid);
unset_user_preference('newemailkey', $userid);
unset_user_preference('newemailattemptsleft', $userid);
}
/**
* Performs the common access checks and page setup for all
* user preference pages.
*
* @param int $userid The user id to edit taken from the page params.
* @param int $courseid The optional course id if we came from a course context.
* @return array containing the user and course records.
*/
function useredit_setup_preference_page($userid, $courseid) {
global $PAGE, $SESSION, $DB, $CFG, $OUTPUT, $USER;
// Guest can not edit.
if (isguestuser()) {
throw new \moodle_exception('guestnoeditprofile');
}
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
throw new \moodle_exception('invalidcourseid');
}
if ($course->id != SITEID) {
require_login($course);
} else if (!isloggedin()) {
if (empty($SESSION->wantsurl)) {
$SESSION->wantsurl = $CFG->wwwroot.'/user/preferences.php';
}
redirect(get_login_url());
} else {
$PAGE->set_context(context_system::instance());
}
// The user profile we are editing.
if (!$user = $DB->get_record('user', array('id' => $userid))) {
throw new \moodle_exception('invaliduserid');
}
// Guest can not be edited.
if (isguestuser($user)) {
throw new \moodle_exception('guestnoeditprofile');
}
// Remote users cannot be edited.
if (is_mnet_remote_user($user)) {
if (user_not_fully_set_up($user, false)) {
$hostwwwroot = $DB->get_field('mnet_host', 'wwwroot', array('id' => $user->mnethostid));
throw new \moodle_exception('usernotfullysetup', 'mnet', '', $hostwwwroot);
}
redirect($CFG->wwwroot . "/user/view.php?course={$course->id}");
}
$systemcontext = context_system::instance();
$personalcontext = context_user::instance($user->id);
// Check access control.
if ($user->id == $USER->id) {
// Editing own profile - require_login() MUST NOT be used here, it would result in infinite loop!
if (!has_capability('moodle/user:editownprofile', $systemcontext)) {
throw new \moodle_exception('cannotedityourprofile');
}
} else {
// Teachers, parents, etc.
require_capability('moodle/user:editprofile', $personalcontext);
// No editing of primary admin!
if (is_siteadmin($user) and !is_siteadmin($USER)) { // Only admins may edit other admins.
throw new \moodle_exception('useradmineditadmin');
}
}
if ($user->deleted) {
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('userdeleted'));
echo $OUTPUT->footer();
die;
}
$PAGE->set_pagelayout('admin');
$PAGE->add_body_class('limitedwidth');
$PAGE->set_context($personalcontext);
if ($USER->id != $user->id) {
$PAGE->navigation->extend_for_user($user);
} else {
if ($node = $PAGE->navigation->find('myprofile', navigation_node::TYPE_ROOTNODE)) {
$node->force_open();
}
}
return array($user, $course);
}
/**
* Loads the given users preferences into the given user object.
*
* @param stdClass $user The user object, modified by reference.
* @param bool $reload
*/
function useredit_load_preferences(&$user, $reload=true) {
global $USER;
if (!empty($user->id)) {
if ($reload and $USER->id == $user->id) {
// Reload preferences in case it was changed in other session.
unset($USER->preference);
}
if ($preferences = get_user_preferences(null, null, $user->id)) {
foreach ($preferences as $name => $value) {
$user->{'preference_'.$name} = $value;
}
}
}
}
/**
* Updates the user preferences for the given user
*
* Only preference that can be updated directly will be updated here. This method is called from various WS
* updating users and should be used when updating user details. Plugins may list preferences that can
* be updated by defining 'user_preferences' callback, {@see core_user::fill_preferences_cache()}
*
* Some parts of code may use user preference table to store internal data, in these cases it is acceptable
* to call set_user_preference()
*
* @param stdClass|array $usernew object or array that has user preferences as attributes with keys starting with preference_
*/
function useredit_update_user_preference($usernew) {
global $USER;
$ua = (array)$usernew;
if (is_object($usernew) && isset($usernew->id) && isset($usernew->deleted) && isset($usernew->confirmed)) {
// This is already a full user object, maybe not completely full but these fields are enough.
$user = $usernew;
} else if (empty($ua['id']) || $ua['id'] == $USER->id) {
// We are updating current user.
$user = $USER;
} else {
// Retrieve user object.
$user = core_user::get_user($ua['id'], '*', MUST_EXIST);
}
foreach ($ua as $key => $value) {
if (strpos($key, 'preference_') === 0) {
$name = substr($key, strlen('preference_'));
if (core_user::can_edit_preference($name, $user)) {
$value = core_user::clean_preference($value, $name);
set_user_preference($name, $value, $user->id);
}
}
}
}
/**
* @deprecated since Moodle 3.2
* @see core_user::update_picture()
*/
function useredit_update_picture() {
throw new coding_exception('useredit_update_picture() can not be used anymore. Please use ' .
'core_user::update_picture() instead.');
}
/**
* Updates the user email bounce + send counts when the user is edited.
*
* @param stdClass $user The current user object.
* @param stdClass $usernew The updated user object.
*/
function useredit_update_bounces($user, $usernew) {
if (!isset($usernew->email)) {
// Locked field.
return;
}
if (!isset($user->email) || $user->email !== $usernew->email) {
set_bounce_count($usernew, true);
set_send_count($usernew, true);
}
}
/**
* Updates the forums a user is tracking when the user is edited.
*
* @param stdClass $user The original user object.
* @param stdClass $usernew The updated user object.
*/
function useredit_update_trackforums($user, $usernew) {
global $CFG;
if (!isset($usernew->trackforums)) {
// Locked field.
return;
}
if ((!isset($user->trackforums) || ($usernew->trackforums != $user->trackforums)) and !$usernew->trackforums) {
require_once($CFG->dirroot.'/mod/forum/lib.php');
forum_tp_delete_read_records($usernew->id);
}
}
/**
* Updates a users interests.
*
* @param stdClass $user
* @param array $interests
*/
function useredit_update_interests($user, $interests) {
core_tag_tag::set_item_tags('core', 'user', $user->id,
context_user::instance($user->id), $interests);
}
/**
* Powerful function that is used by edit and editadvanced to add common form elements/rules/etc.
*
* @param MoodleQuickForm $mform
* @param array $editoroptions
* @param array $filemanageroptions
* @param stdClass $user
*/
function useredit_shared_definition(&$mform, $editoroptions, $filemanageroptions, $user) {
global $CFG, $USER, $DB;
if ($user->id > 0) {
useredit_load_preferences($user, false);
}
$strrequired = get_string('required');
$stringman = get_string_manager();
// Add the necessary names.
foreach (useredit_get_required_name_fields() as $fullname) {
$purpose = user_edit_map_field_purpose($user->id, $fullname);
$mform->addElement('text', $fullname, get_string($fullname), 'maxlength="100" size="30"' . $purpose);
if ($stringman->string_exists('missing'.$fullname, 'core')) {
$strmissingfield = get_string('missing'.$fullname, 'core');
} else {
$strmissingfield = $strrequired;
}
$mform->addRule($fullname, $strmissingfield, 'required', null, 'client');
$mform->setType($fullname, PARAM_NOTAGS);
}
$enabledusernamefields = useredit_get_enabled_name_fields();
// Add the enabled additional name fields.
foreach ($enabledusernamefields as $addname) {
$purpose = user_edit_map_field_purpose($user->id, $addname);
$mform->addElement('text', $addname, get_string($addname), 'maxlength="100" size="30"' . $purpose);
$mform->setType($addname, PARAM_NOTAGS);
}
// Do not show email field if change confirmation is pending.
if ($user->id > 0 and !empty($CFG->emailchangeconfirmation) and !empty($user->preference_newemail)) {
$notice = get_string('emailchangepending', 'auth', $user);
$notice .= '<br /><a href="edit.php?cancelemailchange=1&amp;id='.$user->id.'">'
. get_string('emailchangecancel', 'auth') . '</a>';
$mform->addElement('static', 'emailpending', get_string('email'), $notice);
} else {
$purpose = user_edit_map_field_purpose($user->id, 'email');
$mform->addElement('text', 'email', get_string('email'), 'maxlength="100" size="30"' . $purpose);
$mform->addRule('email', $strrequired, 'required', null, 'client');
$mform->setType('email', PARAM_RAW_TRIMMED);
}
$choices = array();
$choices['0'] = get_string('emaildisplayno');
$choices['1'] = get_string('emaildisplayyes');
$choices['2'] = get_string('emaildisplaycourse');
$mform->addElement('select', 'maildisplay', get_string('emaildisplay'), $choices);
$mform->setDefault('maildisplay', core_user::get_property_default('maildisplay'));
$mform->addHelpButton('maildisplay', 'emaildisplay');
if (get_config('tool_moodlenet', 'enablemoodlenet')) {
$mform->addElement('text', 'moodlenetprofile', get_string('moodlenetprofile', 'user'), 'maxlength="255" size="30"');
$mform->setType('moodlenetprofile', PARAM_NOTAGS);
$mform->addHelpButton('moodlenetprofile', 'moodlenetprofile', 'user');
}
$mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="21"');
$mform->setType('city', PARAM_TEXT);
if (!empty($CFG->defaultcity)) {
$mform->setDefault('city', $CFG->defaultcity);
}
$purpose = user_edit_map_field_purpose($user->id, 'country');
$choices = get_string_manager()->get_list_of_countries();
$choices = array('' => get_string('selectacountry') . '...') + $choices;
$mform->addElement('select', 'country', get_string('selectacountry'), $choices, $purpose);
if (!empty($CFG->country)) {
$mform->setDefault('country', core_user::get_property_default('country'));
}
if (isset($CFG->forcetimezone) and $CFG->forcetimezone != 99) {
$choices = core_date::get_list_of_timezones($CFG->forcetimezone);
$mform->addElement('static', 'forcedtimezone', get_string('timezone'), $choices[$CFG->forcetimezone]);
$mform->addElement('hidden', 'timezone');
$mform->setType('timezone', core_user::get_property_type('timezone'));
} else {
$choices = core_date::get_list_of_timezones($user->timezone, true);
$mform->addElement('select', 'timezone', get_string('timezone'), $choices);
}
if ($user->id < 0) {
$purpose = user_edit_map_field_purpose($user->id, 'lang');
$translations = get_string_manager()->get_list_of_translations();
$mform->addElement('select', 'lang', get_string('preferredlanguage'), $translations, $purpose);
$lang = empty($user->lang) ? $CFG->lang : $user->lang;
$mform->setDefault('lang', $lang);
}
if (!empty($CFG->allowuserthemes)) {
$choices = array();
$choices[''] = get_string('default');
$themes = get_list_of_themes();
foreach ($themes as $key => $theme) {
if (empty($theme->hidefromselector)) {
$choices[$key] = get_string('pluginname', 'theme_'.$theme->name);
}
}
$mform->addElement('select', 'theme', get_string('preferredtheme'), $choices);
}
$mform->addElement('editor', 'description_editor', get_string('userdescription'), null, $editoroptions);
$mform->setType('description_editor', PARAM_RAW);
$mform->addHelpButton('description_editor', 'userdescription');
if (empty($USER->newadminuser)) {
$mform->addElement('header', 'moodle_picture', get_string('pictureofuser'));
$mform->setExpanded('moodle_picture', true);
if (!empty($CFG->enablegravatar)) {
$mform->addElement('html', html_writer::tag('p', get_string('gravatarenabled')));
}
$mform->addElement('static', 'currentpicture', get_string('currentpicture'));
$mform->addElement('checkbox', 'deletepicture', get_string('deletepicture'));
$mform->setDefault('deletepicture', 0);
$mform->addElement('filemanager', 'imagefile', get_string('newpicture'), '', $filemanageroptions);
$mform->addHelpButton('imagefile', 'newpicture');
$mform->addElement('text', 'imagealt', get_string('imagealt'), 'maxlength="100" size="30"');
$mform->setType('imagealt', PARAM_TEXT);
}
// Display user name fields that are not currenlty enabled here if there are any.
$disabledusernamefields = useredit_get_disabled_name_fields($enabledusernamefields);
if (count($disabledusernamefields) > 0) {
$mform->addElement('header', 'moodle_additional_names', get_string('additionalnames'));
foreach ($disabledusernamefields as $allname) {
$purpose = user_edit_map_field_purpose($user->id, $allname);
$mform->addElement('text', $allname, get_string($allname), 'maxlength="100" size="30"' . $purpose);
$mform->setType($allname, PARAM_NOTAGS);
}
}
if (core_tag_tag::is_enabled('core', 'user') and empty($USER->newadminuser)) {
$mform->addElement('header', 'moodle_interests', get_string('interests'));
$mform->addElement('tags', 'interests', get_string('interestslist'),
array('itemtype' => 'user', 'component' => 'core'));
$mform->addHelpButton('interests', 'interestslist');
}
// Moodle optional fields.
$mform->addElement('header', 'moodle_optional', get_string('optional', 'form'));
$mform->addElement('text', 'idnumber', get_string('idnumber'), 'maxlength="255" size="25"');
$mform->setType('idnumber', core_user::get_property_type('idnumber'));
$mform->addElement('text', 'institution', get_string('institution'), 'maxlength="255" size="25"');
$mform->setType('institution', core_user::get_property_type('institution'));
$mform->addElement('text', 'department', get_string('department'), 'maxlength="255" size="25"');
$mform->setType('department', core_user::get_property_type('department'));
$mform->addElement('text', 'phone1', get_string('phone1'), 'maxlength="20" size="25"');
$mform->setType('phone1', core_user::get_property_type('phone1'));
$mform->setForceLtr('phone1');
$mform->addElement('text', 'phone2', get_string('phone2'), 'maxlength="20" size="25"');
$mform->setType('phone2', core_user::get_property_type('phone2'));
$mform->setForceLtr('phone2');
$mform->addElement('text', 'address', get_string('address'), 'maxlength="255" size="25"');
$mform->setType('address', core_user::get_property_type('address'));
}
/**
* Return required user name fields for forms.
*
* @return array required user name fields in order according to settings.
*/
function useredit_get_required_name_fields() {
global $CFG;
// Get the name display format.
$nameformat = $CFG->fullnamedisplay;
// Names that are required fields on user forms.
$necessarynames = array('firstname', 'lastname');
$languageformat = get_string('fullnamedisplay');
// Check that the language string and the $nameformat contain the necessary names.
foreach ($necessarynames as $necessaryname) {
$pattern = "/$necessaryname\b/";
if (!preg_match($pattern, $languageformat)) {
// If the language string has been altered then fall back on the below order.
$languageformat = 'firstname lastname';
}
if (!preg_match($pattern, $nameformat)) {
// If the nameformat doesn't contain the necessary name fields then use the languageformat.
$nameformat = $languageformat;
}
}
// Order all of the name fields in the postion they are written in the fullnamedisplay setting.
$necessarynames = order_in_string($necessarynames, $nameformat);
return $necessarynames;
}
/**
* Gets enabled (from fullnameformate setting) user name fields in appropriate order.
*
* @return array Enabled user name fields.
*/
function useredit_get_enabled_name_fields() {
global $CFG;
// Get all of the other name fields which are not ranked as necessary.
$additionalusernamefields = array_diff(\core_user\fields::get_name_fields(), array('firstname', 'lastname'));
// Find out which additional name fields are actually being used from the fullnamedisplay setting.
$enabledadditionalusernames = array();
foreach ($additionalusernamefields as $enabledname) {
if (strpos($CFG->fullnamedisplay, $enabledname) !== false) {
$enabledadditionalusernames[] = $enabledname;
}
}
// Order all of the name fields in the postion they are written in the fullnamedisplay setting.
$enabledadditionalusernames = order_in_string($enabledadditionalusernames, $CFG->fullnamedisplay);
return $enabledadditionalusernames;
}
/**
* Gets user name fields not enabled from the setting fullnamedisplay.
*
* @param array $enabledadditionalusernames Current enabled additional user name fields.
* @return array Disabled user name fields.
*/
function useredit_get_disabled_name_fields($enabledadditionalusernames = null) {
// If we don't have enabled additional user name information then go and fetch it (try to avoid).
if (!isset($enabledadditionalusernames)) {
$enabledadditionalusernames = useredit_get_enabled_name_fields();
}
// These are the additional fields that are not currently enabled.
$nonusednamefields = array_diff(\core_user\fields::get_name_fields(),
array_merge(array('firstname', 'lastname'), $enabledadditionalusernames));
// It may not be significant anywhere, but for compatibility, this used to return an array
// with keys and values the same.
$result = [];
foreach ($nonusednamefields as $field) {
$result[$field] = $field;
}
return $result;
}
+75
View File
@@ -0,0 +1,75 @@
<?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/>.
/**
* Allows you to edit a users editor preferences
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->libdir.'/gdlib.php');
require_once($CFG->dirroot.'/user/editor_form.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/lib.php');
$userid = optional_param('id', $USER->id, PARAM_INT); // User id.
$courseid = optional_param('course', SITEID, PARAM_INT); // Course id (defaults to Site).
$PAGE->set_url('/user/editor.php', array('id' => $userid, 'course' => $courseid));
list($user, $course) = useredit_setup_preference_page($userid, $courseid);
// Create form.
$editorform = new user_edit_editor_form();
$user->preference_htmleditor = get_user_preferences( 'htmleditor', '', $user->id);
$editorform->set_data($user);
$redirect = new moodle_url("/user/preferences.php", array('userid' => $user->id));
if ($editorform->is_cancelled()) {
redirect($redirect);
} else if ($data = $editorform->get_data()) {
$user->preference_htmleditor = $data->preference_htmleditor;
useredit_update_user_preference($user);
// Trigger event.
\core\event\user_updated::create_from_userid($user->id)->trigger();
redirect($redirect, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
// Display page header.
$streditmyeditor = get_string('editorpreferences');
$userfullname = fullname($user, true);
$PAGE->navbar->includesettingsbase = true;
$PAGE->add_body_class('limitedwidth');
$PAGE->set_title("$course->shortname: $streditmyeditor");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($streditmyeditor);
// Finally display THE form.
$editorform->display();
// And proper footer.
echo $OUTPUT->footer();
+75
View File
@@ -0,0 +1,75 @@
<?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/>.
/**
* Form to edit a users editor preferences.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Class user_edit_editor_form.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_edit_editor_form extends moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG, $COURSE;
$mform = $this->_form;
$editors = editors_get_enabled();
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
if (count($editors) > 1) {
$choices = array('' => get_string('defaulteditor'));
$firsteditor = '';
foreach (array_keys($editors) as $editor) {
if (!$firsteditor) {
$firsteditor = $editor;
}
$choices[$editor] = get_string('pluginname', 'editor_' . $editor);
}
$mform->addElement('select', 'preference_htmleditor', get_string('textediting'), $choices);
$mform->addHelpButton('preference_htmleditor', 'textediting');
$mform->setDefault('preference_htmleditor', '');
} else {
// Empty string means use the first chosen text editor.
$mform->addElement('hidden', 'preference_htmleditor');
$mform->setDefault('preference_htmleditor', '');
$mform->setType('preference_htmleditor', PARAM_PLUGIN);
}
$this->add_action_buttons(true, get_string('savechanges'));
}
}
+95
View File
@@ -0,0 +1,95 @@
<?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/>.
/**
* Change a users email address
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->libdir.'/adminlib.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/lib.php');
$key = required_param('key', PARAM_ALPHANUM);
$id = required_param('id', PARAM_INT);
$PAGE->set_url('/user/emailupdate.php', array('id' => $id, 'key' => $key));
$PAGE->set_context(context_system::instance());
if (!$user = $DB->get_record('user', array('id' => $id))) {
throw new \moodle_exception('invaliduserid');
}
$preferences = get_user_preferences(null, null, $user->id);
$a = new stdClass();
$a->fullname = fullname($user, true);
$stremailupdate = get_string('emailupdate', 'auth', $a);
$PAGE->set_title($stremailupdate);
$PAGE->set_heading(format_string($SITE->fullname) . ": $stremailupdate");
if (empty($preferences['newemailattemptsleft'])) {
redirect("$CFG->wwwroot/user/view.php?id=$user->id");
} else if ($preferences['newemailattemptsleft'] < 1) {
cancel_email_update($user->id);
echo $OUTPUT->header();
echo $OUTPUT->box(get_string('auth_outofnewemailupdateattempts', 'auth'), 'center');
echo $OUTPUT->footer();
} else if ($key == $preferences['newemailkey']) {
$olduser = clone($user);
cancel_email_update($user->id);
$user->email = $preferences['newemail'];
// Detect duplicate before saving.
if (empty($CFG->allowaccountssameemail)) {
// Make a case-insensitive query for the given email address.
$select = $DB->sql_equal('email', ':email', false) . ' AND mnethostid = :mnethostid AND id <> :userid';
$params = array(
'email' => $user->email,
'mnethostid' => $CFG->mnet_localhost_id,
'userid' => $user->id
);
// If there are other user(s) that already have the same email, cancel and redirect.
if ($DB->record_exists_select('user', $select, $params)) {
redirect(new moodle_url('/user/view.php', ['id' => $user->id]), get_string('emailnowexists', 'auth'));
}
}
// Update user email.
$authplugin = get_auth_plugin($user->auth);
$authplugin->user_update($olduser, $user);
user_update_user($user, false);
$a->email = $user->email;
redirect(
new moodle_url('/user/view.php', ['id' => $user->id]),
get_string('emailupdatesuccess', 'auth', $a),
null,
\core\output\notification::NOTIFY_SUCCESS
);
} else {
$preferences['newemailattemptsleft']--;
set_user_preference('newemailattemptsleft', $preferences['newemailattemptsleft'], $user->id);
echo $OUTPUT->header();
echo $OUTPUT->box(get_string('auth_invalidnewemailkey', 'auth'), 'center');
echo $OUTPUT->footer();
}
+2071
View File
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
<?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/>.
/**
* Manage files in folder in private area.
*
* @package core_user
* @category files
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../config.php');
require_login();
if (isguestuser()) {
die();
}
$context = context_user::instance($USER->id);
require_capability('moodle/user:manageownfiles', $context);
$title = get_string('privatefiles');
$PAGE->set_url('/user/files.php');
$PAGE->set_context($context);
$PAGE->set_title($title);
$PAGE->set_heading(fullname($USER));
$PAGE->set_pagelayout('standard');
$PAGE->add_body_class('limitedwidth');
$PAGE->set_pagetype('user-files');
echo $OUTPUT->header();
echo $OUTPUT->heading($title);
echo $OUTPUT->box_start('generalbox');
echo html_writer::start_div('', ['id' => 'userfilesform']);
$form = new \core_user\form\private_files();
$form->set_data_for_dynamic_submission();
$form->display();
echo html_writer::end_div();
$PAGE->requires->js_call_amd('core_user/private_files', 'initDynamicForm',
['#userfilesform', \core_user\form\private_files::class]);
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
+49
View File
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This is filter is used to see which students are enroled on any courses
*
* @package core_user
* @copyright 2014 Krister Viirsaar
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* User filter to distinguish users with no or any enroled courses.
* @copyright 2014 Krister Viirsaar
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_anycourses extends user_filter_yesno {
/**
* Returns the condition to be used with SQL
*
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
$value = $data['value'];
$not = $value ? '' : 'NOT';
return array("EXISTS ( SELECT userid FROM {user_enrolments} ) AND " .
" id $not IN ( SELECT userid FROM {user_enrolments} )", array());
}
}
+139
View File
@@ -0,0 +1,139 @@
<?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/>.
/**
* Generic checkbox filter.
*
* This will create generic filter with checkbox option and can be used for
* disabling other elements for specific condition.
*
* @package core_user
* @category user
* @copyright 2011 Rajesh Taneja
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* Generic filter based for checkbox and can be used for disabling items
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_checkbox extends user_filter_type {
/**
* list of all the fields which needs to be disabled, if checkbox is checked
* @var array
*/
protected $disableelements = array();
/**
* name of user table field/fields on which data needs to be compared
* @var mixed
*/
protected $field;
/**
* Constructor, initalize user_filter_type and sets $disableelements array
* with list of elements to be diabled by checkbox.
*
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
* @param mixed $field user table field/fields name for comparison
* @param array $disableelements name of fields which should be disabled if this checkbox is checked.
*/
public function __construct($name, $label, $advanced, $field, $disableelements=null) {
parent::__construct($name, $label, $advanced);
$this->field = $field;
if (!empty($disableelements)) {
if (!is_array($disableelements)) {
$this->disableelements = array($disableelements);
} else {
$this->disableelements = $disableelements;
}
}
}
/**
* Adds controls specific to this filter in the form.
*
* @param moodleform $mform a MoodleQuickForm object in which element will be added
*/
public function setupForm(&$mform) {
$mform->addElement('checkbox', $this->_name, $this->_label, '');
if ($this->_advanced) {
$mform->setAdvanced($this->_name);
}
// Check if disable if options are set. if yes then set rules.
if (!empty($this->disableelements) && is_array($this->disableelements)) {
foreach ($this->disableelements as $disableelement) {
$mform->disabledIf($disableelement, $this->_name, 'checked');
}
}
}
/**
* Retrieves data from the form data
*
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
// Check if disable if options are set. if yes then don't add this..
if (!empty($this->disableelements) && is_array($this->disableelements)) {
foreach ($this->disableelements as $disableelement) {
if (property_exists($formdata, $disableelement)) {
return false;
}
}
}
if (property_exists($formdata, $field) and $formdata->$field !== '') {
return array('value' => (string)$formdata->$field);
}
return false;
}
/**
* Returns the condition to be used with SQL where
*
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
$field = $this->field;
if (is_array($field)) {
$res = " {$field[0]} = {$field[1]} ";
} else {
$res = " {$field} = 0 ";
}
return array($res, array());
}
/**
* Returns a human friendly description of the filter used as label.
*
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
return $this->_label;
}
}
+198
View File
@@ -0,0 +1,198 @@
<?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/>.
/**
* Cohort filter.
*
* @package core_user
* @category user
* @copyright 2011 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* Generic filter for cohort membership.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_cohort extends user_filter_type {
/**
* Constructor
* @param boolean $advanced advanced form element flag
*/
public function __construct($advanced) {
parent::__construct('cohort', get_string('idnumber', 'core_cohort'), $advanced);
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_cohort($advanced) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($advanced);
}
/**
* Returns an array of comparison operators
* @return array of comparison operators
*/
public function getOperators() {
return array(0 => get_string('contains', 'filters'),
1 => get_string('doesnotcontain', 'filters'),
2 => get_string('isequalto', 'filters'),
3 => get_string('startswith', 'filters'),
4 => get_string('endswith', 'filters'),
5 => get_string('isempty', 'filters'));
}
/**
* Adds controls specific to this filter in the form.
* @param object $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$objs = array();
$objs['select'] = $mform->createElement('select', $this->_name.'_op', null, $this->getOperators());
$objs['text'] = $mform->createElement('text', $this->_name, null);
$objs['select']->setLabel(get_string('limiterfor', 'filters', $this->_label));
$objs['text']->setLabel(get_string('valuefor', 'filters', $this->_label));
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
$mform->setType($this->_name, PARAM_RAW);
$mform->disabledIf($this->_name, $this->_name.'_op', 'eq', 5);
if ($this->_advanced) {
$mform->setAdvanced($this->_name.'_grp');
}
$mform->setDefault($this->_name.'_op', 2);
}
/**
* Retrieves data from the form data
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
$operator = $field.'_op';
if (property_exists($formdata, $operator)) {
if ($formdata->$operator != 5 and $formdata->$field == '') {
// No data - no change except for empty filter.
return false;
}
// If field value is set then use it, else it's null.
$fieldvalue = null;
if (isset($formdata->$field)) {
$fieldvalue = $formdata->$field;
}
return array('operator' => (int)$formdata->$operator, 'value' => $fieldvalue);
}
return false;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
global $DB;
static $counter = 0;
$name = 'ex_cohort'.$counter++;
$operator = $data['operator'];
$value = $data['value'];
$params = array();
if ($value === '') {
return '';
}
$not = '';
switch($operator) {
case 0: // Contains.
$res = $DB->sql_like('idnumber', ":$name", false, false);
$params[$name] = "%$value%";
break;
case 1: // Does not contain.
$not = 'NOT';
$res = $DB->sql_like('idnumber', ":$name", false, false);
$params[$name] = "%$value%";
break;
case 2: // Equal to.
$res = $DB->sql_like('idnumber', ":$name", false, false);
$params[$name] = "$value";
break;
case 3: // Starts with.
$res = $DB->sql_like('idnumber', ":$name", false, false);
$params[$name] = "$value%";
break;
case 4: // Ends with.
$res = $DB->sql_like('idnumber', ":$name", false, false);
$params[$name] = "%$value";
break;
case 5: // Empty.
$not = 'NOT';
$res = '(idnumber IS NOT NULL AND idnumber <> :'.$name.')';
$params[$name] = '';
break;
default:
return '';
}
$sql = "id $not IN (SELECT userid
FROM {cohort_members}
JOIN {cohort} ON {cohort_members}.cohortid = {cohort}.id
WHERE $res)";
return array($sql, $params);
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
$operator = $data['operator'];
$value = $data['value'];
$operators = $this->getOperators();
$a = new stdClass();
$a->label = $this->_label;
$a->value = '"'.s($value).'"';
$a->operator = $operators[$operator];
switch ($operator) {
case 0: // Contains.
case 1: // Doesn't contain.
case 2: // Equal to.
case 3: // Starts with.
case 4: // Ends with.
return get_string('textlabel', 'filters', $a);
case 5: // Empty.
return get_string('textlabelnovalue', 'filters', $a);
}
return '';
}
}
+193
View File
@@ -0,0 +1,193 @@
<?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/>.
/**
* Course role filter
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot .'/user/filters/lib.php');
/**
* User filter based on roles in a course identified by its shortname.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_courserole extends user_filter_type {
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
*/
public function __construct($name, $label, $advanced) {
parent::__construct($name, $label, $advanced);
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_courserole($name, $label, $advanced) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced);
}
/**
* Returns an array of available roles
* @return array of availble roles
*/
public function get_roles() {
$context = context_system::instance();
$roles = array(0 => get_string('anyrole', 'filters')) + get_default_enrol_roles($context);
return $roles;
}
/**
* Returns an array of course categories
* @return array of course categories
*/
public function get_course_categories() {
return array(0 => get_string('anycategory', 'filters')) + core_course_category::make_categories_list();
}
/**
* Adds controls specific to this filter in the form.
* @param moodleform $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$objs = array();
$objs['role'] = $mform->createElement('select', $this->_name .'_rl', null, $this->get_roles());
$objs['role']->setLabel(get_string('courserole', 'filters'));
$objs['category'] = $mform->createElement('select', $this->_name .'_ct', null, $this->get_course_categories());
$objs['category']->setLabel(get_string('coursecategory', 'filters'));
$objs['value'] = $mform->createElement('text', $this->_name, null);
$objs['value']->setLabel(get_string('coursevalue', 'filters'));
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
$mform->setType($this->_name, PARAM_TEXT);
if ($this->_advanced) {
$mform->setAdvanced($this->_name.'_grp');
}
}
/**
* Retrieves data from the form data
* @param stdClass $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
$role = $field .'_rl';
$category = $field .'_ct';
if (property_exists($formdata, $field)) {
if (empty($formdata->$field) and empty($formdata->$role) and empty($formdata->$category)) {
// Nothing selected.
return false;
}
return array('value' => (string)$formdata->$field,
'roleid' => (int)$formdata->$role,
'categoryid' => (int)$formdata->$category);
}
return false;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
global $CFG, $DB;
static $counter = 0;
$pref = 'ex_courserole'.($counter++).'_';
$value = $data['value'];
$roleid = $data['roleid'];
$categoryid = $data['categoryid'];
$params = array();
if (empty($value) and empty($roleid) and empty($categoryid)) {
return array('', $params);
}
$where = "b.contextlevel=50";
if ($roleid) {
$where .= " AND a.roleid = :{$pref}roleid";
$params[$pref.'roleid'] = $roleid;
}
if ($categoryid) {
$where .= " AND c.category = :{$pref}categoryid";
$params[$pref.'categoryid'] = $categoryid;
}
if ($value) {
$where .= " AND c.shortname = :{$pref}course";
$params[$pref.'course'] = $value;
}
return array("id IN (SELECT userid
FROM {role_assignments} a
INNER JOIN {context} b ON a.contextid=b.id
INNER JOIN {course} c ON b.instanceid=c.id
WHERE $where)", $params);
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
global $DB;
$value = $data['value'];
$roleid = $data['roleid'];
$categoryid = $data['categoryid'];
$a = new stdClass();
$a->label = $this->_label;
if ($roleid) {
$role = $DB->get_record('role', array('id' => $roleid));
$a->rolename = '"'.role_get_name($role).'"';
} else {
$a->rolename = get_string('anyrole', 'filters');
}
if ($categoryid) {
$catname = $DB->get_field('course_categories', 'name', array('id' => $categoryid));
$a->categoryname = '"'.format_string($catname).'"';
} else {
$a->categoryname = get_string('anycategory', 'filters');
}
if ($value) {
$a->coursename = '"'.s($value).'"';
if (!$DB->record_exists('course', array('shortname' => $value))) {
return '<span class="notifyproblem">'.get_string('courserolelabelerror', 'filters', $a).'</span>';
}
} else {
$a->coursename = get_string('anycourse', 'filters');
}
return get_string('courserolelabel', 'filters', $a);
}
}
+160
View File
@@ -0,0 +1,160 @@
<?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/>.
/**
* Date filter
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* Generic filter based on a date.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_date extends user_filter_type {
/**
* the fields available for comparisson
* @var string
*/
public $_field;
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
* @param string $field user table filed name
*/
public function __construct($name, $label, $advanced, $field) {
parent::__construct($name, $label, $advanced);
$this->_field = $field;
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_date($name, $label, $advanced, $field) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced, $field);
}
/**
* Adds controls specific to this filter in the form.
* @param object $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$objs = array();
$objs[] = $mform->createElement('static', $this->_name.'_s1', null,
html_writer::start_tag('div', array('class' => 'w-100 d-flex align-items-center')));
$objs[] = $mform->createElement('static', $this->_name.'_s2', null,
html_writer::tag('div', get_string('isafter', 'filters'), array('class' => 'mr-2')));
$objs[] = $mform->createElement('date_selector', $this->_name.'_sdt', null, array('optional' => true));
$objs[] = $mform->createElement('static', $this->_name.'_s3', null, html_writer::end_tag('div'));
$objs[] = $mform->createElement('static', $this->_name.'_s4', null,
html_writer::start_tag('div', array('class' => 'w-100 d-flex align-items-center')));
$objs[] = $mform->createElement('static', $this->_name.'_s5', null,
html_writer::tag('div', get_string('isbefore', 'filters'), array('class' => 'mr-2')));
$objs[] = $mform->createElement('date_selector', $this->_name.'_edt', null, array('optional' => true));
$objs[] = $mform->createElement('static', $this->_name.'_s6', null, html_writer::end_tag('div'));
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
if ($this->_advanced) {
$mform->setAdvanced($this->_name.'_grp');
}
}
/**
* Retrieves data from the form data
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$sdt = $this->_name.'_sdt';
$edt = $this->_name.'_edt';
if (!$formdata->$sdt and !$formdata->$edt) {
return false;
}
$data = array();
$data['after'] = $formdata->$sdt;
$data['before'] = $formdata->$edt;
return $data;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
$after = (int)$data['after'];
$before = (int)$data['before'];
$field = $this->_field;
if (empty($after) and empty($before)) {
return array('', array());
}
$res = " $field >= 0 ";
if ($after) {
$res .= " AND $field >= $after";
}
if ($before) {
$res .= " AND $field <= $before";
}
return array($res, array());
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
$after = $data['after'];
$before = $data['before'];
$field = $this->_field;
$a = new stdClass();
$a->label = $this->_label;
$a->after = userdate($after);
$a->before = userdate($before);
if ($after and $before) {
return get_string('datelabelisbetween', 'filters', $a);
} else if ($after) {
return get_string('datelabelisafter', 'filters', $a);
} else if ($before) {
return get_string('datelabelisbefore', 'filters', $a);
}
return '';
}
}
+124
View File
@@ -0,0 +1,124 @@
<?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/>.
/**
* Global role filter
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* User filter based on global roles.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_globalrole extends user_filter_type {
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
*/
public function __construct($name, $label, $advanced) {
parent::__construct($name, $label, $advanced);
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_globalrole($name, $label, $advanced) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced);
}
/**
* Returns an array of available roles
* @return array of availble roles
*/
public function get_roles() {
$context = context_system::instance();
$roles = array(0 => get_string('anyrole', 'filters')) + get_assignable_roles($context);
return $roles;
}
/**
* Adds controls specific to this filter in the form.
* @param object $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$obj =& $mform->addElement('select', $this->_name, $this->_label, $this->get_roles());
$mform->setDefault($this->_name, 0);
if ($this->_advanced) {
$mform->setAdvanced($this->_name);
}
}
/**
* Retrieves data from the form data
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
if (property_exists($formdata, $field) and !empty($formdata->$field)) {
return array('value' => (int)$formdata->$field);
}
return false;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
global $CFG;
$value = (int)$data['value'];
$timenow = round(time(), 100);
$sql = "id IN (SELECT userid
FROM {role_assignments} a
WHERE a.contextid=".SYSCONTEXTID." AND a.roleid=$value)";
return array($sql, array());
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
global $DB;
$role = $DB->get_record('role', array('id' => $data['value']));
$a = new stdClass();
$a->label = $this->_label;
$a->value = '"'.role_get_name($role).'"';
return get_string('globalrolelabel', 'filters', $a);
}
}
+361
View File
@@ -0,0 +1,361 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains the User Filter API.
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/text.php');
require_once($CFG->dirroot.'/user/filters/date.php');
require_once($CFG->dirroot.'/user/filters/select.php');
require_once($CFG->dirroot.'/user/filters/simpleselect.php');
require_once($CFG->dirroot.'/user/filters/courserole.php');
require_once($CFG->dirroot.'/user/filters/globalrole.php');
require_once($CFG->dirroot.'/user/filters/profilefield.php');
require_once($CFG->dirroot.'/user/filters/yesno.php');
require_once($CFG->dirroot.'/user/filters/anycourses.php');
require_once($CFG->dirroot.'/user/filters/cohort.php');
require_once($CFG->dirroot.'/user/filters/user_filter_forms.php');
require_once($CFG->dirroot.'/user/filters/checkbox.php');
/**
* User filtering wrapper class.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filtering {
/** @var array */
public $_fields;
/** @var \user_add_filter_form */
public $_addform;
/** @var \user_active_filter_form */
public $_activeform;
/**
* Contructor
* @param array $fieldnames array of visible user fields
* @param string $baseurl base url used for submission/return, null if the same of current page
* @param array $extraparams extra page parameters
*/
public function __construct($fieldnames = null, $baseurl = null, $extraparams = null) {
global $SESSION;
if (!isset($SESSION->user_filtering)) {
$SESSION->user_filtering = array();
}
if (empty($fieldnames)) {
// As a start, add all fields as advanced fields (which are only available after clicking on "Show more").
$fieldnames = array('realname' => 1, 'lastname' => 1, 'firstname' => 1, 'username' => 1, 'email' => 1, 'city' => 1,
'country' => 1, 'confirmed' => 1, 'suspended' => 1, 'profile' => 1, 'courserole' => 1,
'anycourses' => 1, 'systemrole' => 1, 'cohort' => 1, 'firstaccess' => 1, 'lastaccess' => 1,
'neveraccessed' => 1, 'timecreated' => 1, 'timemodified' => 1, 'nevermodified' => 1, 'auth' => 1,
'mnethostid' => 1, 'idnumber' => 1, 'institution' => 1, 'department' => 1, 'lastip' => 1);
// Get the config which filters the admin wanted to show by default.
$userfiltersdefault = get_config('core', 'userfiltersdefault');
// If the admin did not enable any filter, the form will not make much sense if all fields are hidden behind
// "Show more". Thus, we enable the 'realname' filter automatically.
if ($userfiltersdefault == '') {
$userfiltersdefault = array('realname');
// Otherwise, we split the enabled filters into an array.
} else {
$userfiltersdefault = explode(',', $userfiltersdefault);
}
// Show these fields by default which the admin has enabled in the config.
foreach ($userfiltersdefault as $key) {
$fieldnames[$key] = 0;
}
}
$this->_fields = array();
foreach ($fieldnames as $fieldname => $advanced) {
if ($field = $this->get_field($fieldname, $advanced)) {
$this->_fields[$fieldname] = $field;
}
}
// Fist the new filter form.
$this->_addform = new user_add_filter_form($baseurl, array('fields' => $this->_fields, 'extraparams' => $extraparams));
if ($adddata = $this->_addform->get_data()) {
// Clear previous filters.
if (!empty($adddata->replacefilters)) {
$SESSION->user_filtering = [];
}
// Add new filters.
foreach ($this->_fields as $fname => $field) {
$data = $field->check_data($adddata);
if ($data === false) {
continue; // Nothing new.
}
if (!array_key_exists($fname, $SESSION->user_filtering)) {
$SESSION->user_filtering[$fname] = array();
}
$SESSION->user_filtering[$fname][] = $data;
}
}
// Now the active filters.
$this->_activeform = new user_active_filter_form($baseurl, array('fields' => $this->_fields, 'extraparams' => $extraparams));
if ($activedata = $this->_activeform->get_data()) {
if (!empty($activedata->removeall)) {
$SESSION->user_filtering = array();
} else if (!empty($activedata->removeselected) and !empty($activedata->filter)) {
foreach ($activedata->filter as $fname => $instances) {
foreach ($instances as $i => $val) {
if (empty($val)) {
continue;
}
unset($SESSION->user_filtering[$fname][$i]);
}
if (empty($SESSION->user_filtering[$fname])) {
unset($SESSION->user_filtering[$fname]);
}
}
}
}
// Rebuild the forms if filters data was processed.
if ($adddata || $activedata) {
$_POST = []; // Reset submitted data.
$this->_addform = new user_add_filter_form($baseurl, ['fields' => $this->_fields, 'extraparams' => $extraparams]);
$this->_activeform = new user_active_filter_form($baseurl, ['fields' => $this->_fields, 'extraparams' => $extraparams]);
}
}
/**
* Creates known user filter if present
* @param string $fieldname
* @param boolean $advanced
* @return object filter
*/
public function get_field($fieldname, $advanced) {
global $USER, $CFG, $DB, $SITE;
switch ($fieldname) {
case 'username': return new user_filter_text('username', get_string('username'), $advanced, 'username');
case 'realname': return new user_filter_text('realname', get_string('fullnameuser'), $advanced, $DB->sql_fullname());
case 'lastname': return new user_filter_text('lastname', get_string('lastname'), $advanced, 'lastname');
case 'firstname': return new user_filter_text('firstname', get_string('firstname'), $advanced, 'firstname');
case 'email': return new user_filter_text('email', get_string('email'), $advanced, 'email');
case 'city': return new user_filter_text('city', get_string('city'), $advanced, 'city');
case 'country': return new user_filter_select('country', get_string('country'), $advanced, 'country', get_string_manager()->get_list_of_countries(), $USER->country);
case 'confirmed': return new user_filter_yesno('confirmed', get_string('confirmed', 'admin'), $advanced, 'confirmed');
case 'suspended': return new user_filter_yesno('suspended', get_string('suspended', 'auth'), $advanced, 'suspended');
case 'profile': return new user_filter_profilefield('profile', get_string('profilefields', 'admin'), $advanced);
case 'courserole': return new user_filter_courserole('courserole', get_string('courserole', 'filters'), $advanced);
case 'anycourses':
return new user_filter_anycourses('anycourses', get_string('anycourses', 'filters'), $advanced, 'user_enrolments');
case 'systemrole': return new user_filter_globalrole('systemrole', get_string('globalrole', 'role'), $advanced);
case 'firstaccess': return new user_filter_date('firstaccess', get_string('firstaccess', 'filters'), $advanced, 'firstaccess');
case 'lastaccess': return new user_filter_date('lastaccess', get_string('lastaccess'), $advanced, 'lastaccess');
case 'neveraccessed': return new user_filter_checkbox('neveraccessed', get_string('neveraccessed', 'filters'), $advanced, 'firstaccess', array('lastaccess_sck', 'lastaccess_eck', 'firstaccess_eck', 'firstaccess_sck'));
case 'timecreated': return new user_filter_date('timecreated', get_string('timecreated'), $advanced, 'timecreated');
case 'timemodified': return new user_filter_date('timemodified', get_string('lastmodified'), $advanced, 'timemodified');
case 'nevermodified': return new user_filter_checkbox('nevermodified', get_string('nevermodified', 'filters'), $advanced, array('timemodified', 'timecreated'), array('timemodified_sck', 'timemodified_eck'));
case 'cohort': return new user_filter_cohort($advanced);
case 'idnumber': return new user_filter_text('idnumber', get_string('idnumber'), $advanced, 'idnumber');
case 'institution': return new user_filter_text('institution', get_string('institution'), $advanced, 'institution');
case 'department': return new user_filter_text('department', get_string('department'), $advanced, 'department');
case 'lastip': return new user_filter_text('lastip', get_string('lastip'), $advanced, 'lastip');
case 'auth':
$plugins = core_component::get_plugin_list('auth');
$choices = array();
foreach ($plugins as $auth => $unused) {
$choices[$auth] = get_string('pluginname', "auth_{$auth}");
}
return new user_filter_simpleselect('auth', get_string('authentication'), $advanced, 'auth', $choices);
case 'mnethostid':
// Include all hosts even those deleted or otherwise problematic.
if (!$hosts = $DB->get_records('mnet_host', null, 'id', 'id, wwwroot, name')) {
$hosts = array();
}
$choices = array();
foreach ($hosts as $host) {
if ($host->id == $CFG->mnet_localhost_id) {
$choices[$host->id] = format_string($SITE->fullname).' ('.get_string('local').')';
} else if (empty($host->wwwroot)) {
// All hosts.
continue;
} else {
$choices[$host->id] = $host->name.' ('.$host->wwwroot.')';
}
}
if ($usedhosts = $DB->get_fieldset_sql("SELECT DISTINCT mnethostid FROM {user} WHERE deleted=0")) {
foreach ($usedhosts as $hostid) {
if (empty($hosts[$hostid])) {
$choices[$hostid] = 'id: '.$hostid.' ('.get_string('error').')';
}
}
}
if (count($choices) < 2) {
return null; // Filter not needed.
}
return new user_filter_simpleselect('mnethostid', get_string('mnetidprovider', 'mnet'), $advanced, 'mnethostid', $choices);
default:
return null;
}
}
/**
* Returns sql where statement based on active user filters
* @param string $extra sql
* @param array $params named params (recommended prefix ex)
* @return array sql string and $params
*/
public function get_sql_filter($extra='', array $params=null) {
global $SESSION;
$sqls = array();
if ($extra != '') {
$sqls[] = $extra;
}
$params = (array)$params;
if (!empty($SESSION->user_filtering)) {
foreach ($SESSION->user_filtering as $fname => $datas) {
if (!array_key_exists($fname, $this->_fields)) {
continue; // Filter not used.
}
$field = $this->_fields[$fname];
foreach ($datas as $i => $data) {
list($s, $p) = $field->get_sql_filter($data);
$sqls[] = $s;
$params = $params + $p;
}
}
}
if (empty($sqls)) {
return array('', array());
} else {
$sqls = implode(' AND ', $sqls);
return array($sqls, $params);
}
}
/**
* Print the add filter form.
*/
public function display_add() {
$this->_addform->display();
}
/**
* Print the active filter form.
*/
public function display_active() {
$this->_activeform->display();
}
}
/**
* The base user filter class. All abstract classes must be implemented.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_type {
/**
* The name of this filter instance.
* @var string
*/
public $_name;
/**
* The label of this filter instance.
* @var string
*/
public $_label;
/**
* Advanced form element flag
* @var bool
*/
public $_advanced;
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
*/
public function __construct($name, $label, $advanced) {
$this->_name = $name;
$this->_label = $label;
$this->_advanced = $advanced;
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_type($name, $label, $advanced) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced);
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return string the filtering condition or null if the filter is disabled
*/
public function get_sql_filter($data) {
throw new \moodle_exception('mustbeoveride', 'debug', '', 'get_sql_filter');
}
/**
* Retrieves data from the form data
* @param stdClass $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
throw new \moodle_exception('mustbeoveride', 'debug', '', 'check_data');
}
/**
* Adds controls specific to this filter in the form.
* @param moodleform $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
throw new \moodle_exception('mustbeoveride', 'debug', '', 'setupForm');
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
throw new \moodle_exception('mustbeoveride', 'debug', '', 'get_label');
}
}
+255
View File
@@ -0,0 +1,255 @@
<?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/>.
/**
* Profile field filter.
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* User filter based on values of custom profile fields.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_profilefield extends user_filter_type {
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
*/
public function __construct($name, $label, $advanced) {
parent::__construct($name, $label, $advanced);
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_profilefield($name, $label, $advanced) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced);
}
/**
* Returns an array of comparison operators
* @return array of comparison operators
*/
public function get_operators() {
return array(0 => get_string('contains', 'filters'),
1 => get_string('doesnotcontain', 'filters'),
2 => get_string('isequalto', 'filters'),
3 => get_string('startswith', 'filters'),
4 => get_string('endswith', 'filters'),
5 => get_string('isempty', 'filters'),
6 => get_string('isnotdefined', 'filters'),
7 => get_string('isdefined', 'filters'));
}
/**
* Returns an array of custom profile fields
* @return array of profile fields
*/
public function get_profile_fields() {
global $CFG;
require_once($CFG->dirroot . '/user/profile/lib.php');
$fieldrecords = profile_get_custom_fields();
foreach ($fieldrecords as $key => $fieldrecord) {
$fieldrecords[$key]->name = format_string($fieldrecords[$key]->name, false, ['context' => context_system::instance()]);
}
$fields = array_combine(array_keys($fieldrecords), array_column($fieldrecords, 'name'));
core_collator::asort($fields);
$res = array(0 => get_string('anyfield', 'filters'));
return $res + $fields;
}
/**
* Adds controls specific to this filter in the form.
* @param object $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$profilefields = $this->get_profile_fields();
if (empty($profilefields)) {
return;
}
$objs = array();
$objs['field'] = $mform->createElement('select', $this->_name.'_fld', null, $profilefields);
$objs['op'] = $mform->createElement('select', $this->_name.'_op', null, $this->get_operators());
$objs['value'] = $mform->createElement('text', $this->_name, null);
$objs['field']->setLabel(get_string('profilefilterfield', 'filters'));
$objs['op']->setLabel(get_string('profilefilterlimiter', 'filters'));
$objs['value']->setLabel(get_string('valuefor', 'filters', $this->_label));
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
$mform->setType($this->_name, PARAM_RAW);
if ($this->_advanced) {
$mform->setAdvanced($this->_name.'_grp');
}
}
/**
* Retrieves data from the form data
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$profilefields = $this->get_profile_fields();
if (empty($profilefields)) {
return false;
}
$field = $this->_name;
$operator = $field.'_op';
$profile = $field.'_fld';
if (property_exists($formdata, $profile)) {
if ($formdata->$operator < 5 and $formdata->$field === '') {
return false;
}
return array('value' => (string)$formdata->$field,
'operator' => (int)$formdata->$operator,
'profile' => (int)$formdata->$profile);
}
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
global $CFG, $DB;
static $counter = 0;
$name = 'ex_profilefield'.$counter++;
$profilefields = $this->get_profile_fields();
if (empty($profilefields)) {
return '';
}
$profile = $data['profile'];
$operator = $data['operator'];
$value = $data['value'];
$params = array();
if (!array_key_exists($profile, $profilefields)) {
return array('', array());
}
$where = "";
$op = " IN ";
if ($operator < 5 and $value === '') {
return '';
}
switch($operator) {
case 0: // Contains.
$where = $DB->sql_like('data', ":$name", false, false);
$params[$name] = "%$value%";
break;
case 1: // Does not contain.
$where = $DB->sql_like('data', ":$name", false, false, true);
$params[$name] = "%$value%";
break;
case 2: // Equal to.
$where = $DB->sql_like('data', ":$name", false, false);
$params[$name] = "$value";
break;
case 3: // Starts with.
$where = $DB->sql_like('data', ":$name", false, false);
$params[$name] = "$value%";
break;
case 4: // Ends with.
$where = $DB->sql_like('data', ":$name", false, false);
$params[$name] = "%$value";
break;
case 5: // Empty.
$where = "data = :$name";
$params[$name] = "";
break;
case 6: // Is not defined.
$op = " NOT IN ";
break;
case 7: // Is defined.
break;
}
if ($profile) {
if ($where !== '') {
$where = " AND $where";
}
$where = "fieldid=$profile $where";
}
if ($where !== '') {
$where = "WHERE $where";
}
return array("id $op (SELECT userid FROM {user_info_data} $where)", $params);
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
$operators = $this->get_operators();
$profilefields = $this->get_profile_fields();
if (empty($profilefields)) {
return '';
}
$profile = $data['profile'];
$operator = $data['operator'];
$value = $data['value'];
if (!array_key_exists($profile, $profilefields)) {
return '';
}
$a = new stdClass();
$a->label = $this->_label;
$a->value = $value;
$a->profile = $profilefields[$profile];
$a->operator = $operators[$operator];
switch($operator) {
case 0: // Contains.
case 1: // Doesn't contain.
case 2: // Equal to.
case 3: // Starts with.
case 4: // Ends with.
return get_string('profilelabel', 'filters', $a);
case 5: // Empty.
case 6: // Is not defined.
case 7: // Is defined.
return get_string('profilelabelnovalue', 'filters', $a);
}
return '';
}
}
+171
View File
@@ -0,0 +1,171 @@
<?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/>.
/**
* Value select filter.
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* Generic filter based on a list of values.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_select extends user_filter_type {
/**
* options for the list values
* @var array
*/
public $_options;
/** @var string */
public $_field;
/** @var mixed|null */
public $_default;
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
* @param string $field user table filed name
* @param array $options select options
* @param mixed $default option
*/
public function __construct($name, $label, $advanced, $field, $options, $default=null) {
parent::__construct($name, $label, $advanced);
$this->_field = $field;
$this->_options = $options;
$this->_default = $default;
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_select($name, $label, $advanced, $field, $options, $default=null) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced, $field, $options, $default=null);
}
/**
* Returns an array of comparison operators
* @return array of comparison operators
*/
public function get_operators() {
return array(0 => get_string('isanyvalue', 'filters'),
1 => get_string('isequalto', 'filters'),
2 => get_string('isnotequalto', 'filters'));
}
/**
* Adds controls specific to this filter in the form.
* @param moodleform $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$objs = array();
$objs['limiter'] = $mform->createElement('select', $this->_name.'_op', null, $this->get_operators());
$objs['limiter']->setLabel(get_string('limiterfor', 'filters', $this->_label));
$objs['country'] = $mform->createElement('select', $this->_name, null, $this->_options);
$objs['country']->setLabel(get_string('valuefor', 'filters', $this->_label));
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
$mform->disabledIf($this->_name, $this->_name.'_op', 'eq', 0);
if (!is_null($this->_default)) {
$mform->setDefault($this->_name, $this->_default);
}
if ($this->_advanced) {
$mform->setAdvanced($this->_name.'_grp');
}
}
/**
* Retrieves data from the form data
* @param stdClass $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
$operator = $field.'_op';
if (property_exists($formdata, $field) and !empty($formdata->$operator)) {
return array('operator' => (int)$formdata->$operator,
'value' => (string)$formdata->$field);
}
return false;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
static $counter = 0;
$name = 'ex_select'.$counter++;
$operator = $data['operator'];
$value = $data['value'];
$field = $this->_field;
$params = array();
switch($operator) {
case 1: // Equal to.
$res = "=:$name";
$params[$name] = $value;
break;
case 2: // Not equal to.
$res = "<>:$name";
$params[$name] = $value;
break;
default:
return array('', array());
}
return array($field.$res, $params);
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
$operators = $this->get_operators();
$operator = $data['operator'];
$value = $data['value'];
if (empty($operator)) {
return '';
}
$a = new stdClass();
$a->label = $this->_label;
$a->value = '"'.s($this->_options[$value]).'"';
$a->operator = $operators[$operator];
return get_string('selectlabel', 'filters', $a);
}
}
+130
View File
@@ -0,0 +1,130 @@
<?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/>.
/**
* Simple value select filter.
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* Generic filter based on a list of values.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_simpleselect extends user_filter_type {
/**
* options for the list values
* @var array
*/
public $_options;
/**
* @var string
*/
public $_field;
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
* @param string $field user table filed name
* @param array $options select options
*/
public function __construct($name, $label, $advanced, $field, $options) {
parent::__construct($name, $label, $advanced);
$this->_field = $field;
$this->_options = $options;
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_simpleselect($name, $label, $advanced, $field, $options) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced, $field, $options);
}
/**
* Adds controls specific to this filter in the form.
* @param moodleform $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$choices = array('' => get_string('anyvalue', 'filters')) + $this->_options;
$mform->addElement('select', $this->_name, $this->_label, $choices);
if ($this->_advanced) {
$mform->setAdvanced($this->_name);
}
}
/**
* Retrieves data from the form data
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
if (property_exists($formdata, $field) and $formdata->$field !== '') {
return array('value' => (string)$formdata->$field);
}
return false;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
static $counter = 0;
$name = 'ex_simpleselect'.$counter++;
$value = $data['value'];
$params = array();
$field = $this->_field;
if ($value == '') {
return '';
}
return array("$field=:$name", array($name => $value));
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
$value = $data['value'];
$a = new stdClass();
$a->label = $this->_label;
$a->value = '"'.s($this->_options[$value]).'"';
$a->operator = get_string('isequalto', 'filters');
return get_string('selectlabel', 'filters', $a);
}
}
+200
View File
@@ -0,0 +1,200 @@
<?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/>.
/**
* Text field filter
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot.'/user/filters/lib.php');
/**
* Generic filter for text fields.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_text extends user_filter_type {
/** @var string */
public $_field;
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
* @param string $field user table filed name
*/
public function __construct($name, $label, $advanced, $field) {
parent::__construct($name, $label, $advanced);
$this->_field = $field;
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_text($name, $label, $advanced, $field) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced, $field);
}
/**
* Returns an array of comparison operators
* @return array of comparison operators
*/
public function getOperators() {
return array(0 => get_string('contains', 'filters'),
1 => get_string('doesnotcontain', 'filters'),
2 => get_string('isequalto', 'filters'),
3 => get_string('startswith', 'filters'),
4 => get_string('endswith', 'filters'),
5 => get_string('isempty', 'filters'));
}
/**
* Adds controls specific to this filter in the form.
* @param object $mform a MoodleForm object to setup
*/
public function setupForm(&$mform) {
$objs = array();
$objs['select'] = $mform->createElement('select', $this->_name.'_op', null, $this->getOperators());
$objs['text'] = $mform->createElement('text', $this->_name, null);
$objs['select']->setLabel(get_string('limiterfor', 'filters', $this->_label));
$objs['text']->setLabel(get_string('valuefor', 'filters', $this->_label));
$grp =& $mform->addElement('group', $this->_name.'_grp', $this->_label, $objs, '', false);
$mform->setType($this->_name, PARAM_RAW);
$mform->disabledIf($this->_name, $this->_name.'_op', 'eq', 5);
if ($this->_advanced) {
$mform->setAdvanced($this->_name.'_grp');
}
}
/**
* Retrieves data from the form data
* @param object $formdata data submited with the form
* @return mixed array filter data or false when filter not set
*/
public function check_data($formdata) {
$field = $this->_name;
$operator = $field.'_op';
if (property_exists($formdata, $operator)) {
if ($formdata->$operator != 5 and $formdata->$field == '') {
// No data - no change except for empty filter.
return false;
}
// If field value is set then use it, else it's null.
$fieldvalue = null;
if (isset($formdata->$field)) {
$fieldvalue = $formdata->$field;
// If we aren't doing a whitespace comparison, an exact match, trim will give us a better result set.
$trimmed = trim($fieldvalue);
if ($trimmed !== '' && $formdata->$operator != 2) {
$fieldvalue = $trimmed;
}
}
return array('operator' => (int)$formdata->$operator, 'value' => $fieldvalue);
}
return false;
}
/**
* Returns the condition to be used with SQL where
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
global $DB;
static $counter = 0;
$name = 'ex_text'.$counter++;
$operator = $data['operator'];
$value = $data['value'];
$field = $this->_field;
$params = array();
if ($operator != 5 and $value === '') {
return '';
}
switch($operator) {
case 0: // Contains.
$res = $DB->sql_like($field, ":$name", false, false);
$params[$name] = "%$value%";
break;
case 1: // Does not contain.
$res = $DB->sql_like($field, ":$name", false, false, true);
$params[$name] = "%$value%";
break;
case 2: // Equal to.
$res = $DB->sql_like($field, ":$name", false, false);
$params[$name] = "$value";
break;
case 3: // Starts with.
$res = $DB->sql_like($field, ":$name", false, false);
$params[$name] = "$value%";
break;
case 4: // Ends with.
$res = $DB->sql_like($field, ":$name", false, false);
$params[$name] = "%$value";
break;
case 5: // Empty.
$res = "$field = :$name";
$params[$name] = '';
break;
default:
return '';
}
return array($res, $params);
}
/**
* Returns a human friendly description of the filter used as label.
* @param array $data filter settings
* @return string active filter label
*/
public function get_label($data) {
$operator = $data['operator'];
$value = $data['value'];
$operators = $this->getOperators();
$a = new stdClass();
$a->label = $this->_label;
$a->value = '"'.s($value).'"';
$a->operator = $operators[$operator];
switch ($operator) {
case 0: // Contains.
case 1: // Doesn't contain.
case 2: // Equal to.
case 3: // Starts with.
case 4: // Ends with.
return get_string('textlabel', 'filters', $a);
case 5: // Empty.
return get_string('textlabelnovalue', 'filters', $a);
}
return '';
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file contains forms used to filter user.
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->libdir.'/formslib.php');
/**
* Class user_add_filter_form
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_add_filter_form extends moodleform {
/**
* Form definition.
*/
public function definition() {
global $SESSION;
$mform =& $this->_form;
$fields = $this->_customdata['fields'];
$extraparams = $this->_customdata['extraparams'];
$mform->addElement('header', 'newfilter', get_string('newfilter', 'filters'));
foreach ($fields as $ft) {
$ft->setupForm($mform);
}
// In case we wasnt to track some page params.
if ($extraparams) {
foreach ($extraparams as $key => $value) {
$mform->addElement('hidden', $key, $value);
$mform->setType($key, PARAM_RAW);
}
}
// Add buttons.
$replacefiltersbutton = $mform->createElement('submit', 'replacefilters', get_string('replacefilters', 'filters'));
$addfilterbutton = $mform->createElement('submit', 'addfilter', get_string('addfilter', 'filters'));
$buttons = array_filter([
empty($SESSION->user_filtering) ? null : $replacefiltersbutton,
$addfilterbutton,
]);
$mform->addGroup($buttons);
}
}
/**
* Class user_active_filter_form
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_active_filter_form extends moodleform {
/**
* Form definition.
*/
public function definition() {
global $SESSION; // This is very hacky :-(.
$mform =& $this->_form;
$fields = $this->_customdata['fields'];
$extraparams = $this->_customdata['extraparams'];
if (!empty($SESSION->user_filtering)) {
// Add controls for each active filter in the active filters group.
$mform->addElement('header', 'actfilterhdr', get_string('actfilterhdr', 'filters'));
foreach ($SESSION->user_filtering as $fname => $datas) {
if (!array_key_exists($fname, $fields)) {
continue; // Filter not used.
}
$field = $fields[$fname];
foreach ($datas as $i => $data) {
$description = $field->get_label($data);
$mform->addElement('checkbox', 'filter['.$fname.']['.$i.']', null, $description);
}
}
if ($extraparams) {
foreach ($extraparams as $key => $value) {
$mform->addElement('hidden', $key, $value);
$mform->setType($key, PARAM_RAW);
}
}
$objs = array();
$objs[] = &$mform->createElement('submit', 'removeselected', get_string('removeselected', 'filters'));
$objs[] = &$mform->createElement('submit', 'removeall', get_string('removeall', 'filters'));
$mform->addElement('group', 'actfiltergrp', '', $objs, ' ', false);
}
}
}
+71
View File
@@ -0,0 +1,71 @@
<?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/>.
/**
* Yes/No (boolean) filter.
*
* @package core_user
* @category user
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Generic yes/no filter with radio buttons for integer fields.
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_filter_yesno extends user_filter_simpleselect {
/**
* Constructor
* @param string $name the name of the filter instance
* @param string $label the label of the filter instance
* @param boolean $advanced advanced form element flag
* @param string $field user table filed name
*/
public function __construct($name, $label, $advanced, $field) {
parent::__construct($name, $label, $advanced, $field, array(0 => get_string('no'), 1 => get_string('yes')));
}
/**
* Old syntax of class constructor. Deprecated in PHP7.
*
* @deprecated since Moodle 3.1
*/
public function user_filter_yesno($name, $label, $advanced, $field) {
debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
self::__construct($name, $label, $advanced, $field);
}
/**
* Returns the condition to be used with SQL
*
* @param array $data filter settings
* @return array sql string and $params
*/
public function get_sql_filter($data) {
static $counter = 0;
$name = 'ex_yesno'.$counter++;
$value = $data['value'];
$field = $this->_field;
if ($value == '') {
return array();
}
return array("$field=:$name", array($name => $value));
}
}
+95
View File
@@ -0,0 +1,95 @@
<?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/>.
/**
* Allows you to edit a users forum preferences
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->libdir.'/gdlib.php');
require_once($CFG->dirroot.'/user/forum_form.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/lib.php');
$userid = optional_param('id', $USER->id, PARAM_INT); // User id.
$courseid = optional_param('course', SITEID, PARAM_INT); // Course id (defaults to Site).
$PAGE->set_url('/user/forum.php', array('id' => $userid, 'course' => $courseid));
list($user, $course) = useredit_setup_preference_page($userid, $courseid);
// Create form.
$forumform = new user_edit_forum_form(null, array('userid' => $user->id));
$user->markasreadonnotification = get_user_preferences('forum_markasreadonnotification', 1, $user->id);
$user->useexperimentalui = get_user_preferences('forum_useexperimentalui', 0, $user->id);
$forumform->set_data($user);
$redirect = new moodle_url("/user/preferences.php", array('userid' => $user->id));
if ($forumform->is_cancelled()) {
redirect($redirect);
} else if ($data = $forumform->get_data()) {
$user->maildigest = $data->maildigest;
$user->autosubscribe = $data->autosubscribe;
$user->preference_forum_useexperimentalui = $data->useexperimentalui;
if (!empty($CFG->forum_trackreadposts)) {
$user->trackforums = $data->trackforums;
if (property_exists($data, 'markasreadonnotification')) {
$user->preference_forum_markasreadonnotification = $data->markasreadonnotification;
}
}
unset($user->markasreadonnotification);
useredit_update_user_preference($user);
user_update_user($user, false, false);
// Trigger event.
\core\event\user_updated::create_from_userid($user->id)->trigger();
if ($USER->id == $user->id) {
$USER->maildigest = $data->maildigest;
$USER->autosubscribe = $data->autosubscribe;
if (!empty($CFG->forum_trackreadposts)) {
$USER->trackforums = $data->trackforums;
}
}
redirect($redirect, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
// Display page header.
$streditmyforum = get_string('forumpreferences');
$userfullname = fullname($user, true);
$PAGE->navbar->includesettingsbase = true;
$PAGE->add_body_class('limitedwidth');
$PAGE->set_title("$course->shortname: $streditmyforum");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($streditmyforum);
// Finally display THE form.
$forumform->display();
// And proper footer.
echo $OUTPUT->footer();
+98
View File
@@ -0,0 +1,98 @@
<?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/>.
/**
* Form to edit a users forum preferences.
*
* These are stored as columns in the user table, which
* is why they are in /user and not /mod/forum.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Class user_edit_forum_form.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_edit_forum_form extends moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG, $COURSE;
$mform = $this->_form;
$choices = array();
$choices['0'] = get_string('emaildigestoff');
$choices['1'] = get_string('emaildigestcomplete');
$choices['2'] = get_string('emaildigestsubjects');
$mform->addElement('select', 'maildigest', get_string('emaildigest'), $choices);
$mform->setDefault('maildigest', core_user::get_property_default('maildigest'));
$mform->addHelpButton('maildigest', 'emaildigest');
$choices = array();
$choices['1'] = get_string('autosubscribeyes');
$choices['0'] = get_string('autosubscribeno');
$mform->addElement('select', 'autosubscribe', get_string('autosubscribe'), $choices);
$mform->setDefault('autosubscribe', core_user::get_property_default('autosubscribe'));
$choices = array();
$choices['1'] = get_string('yes');
$choices['0'] = get_string('no');
$mform->addElement('select', 'useexperimentalui', get_string('useexperimentalui', 'mod_forum'), $choices);
$mform->setDefault('useexperimentalui', '0');
if (!empty($CFG->forum_trackreadposts)) {
$mform->addElement('header', 'trackreadposts', get_string('trackreadposts_header', 'mod_forum'));
$choices = array();
$choices['0'] = get_string('trackforumsno');
$choices['1'] = get_string('trackforumsyes');
$mform->addElement('select', 'trackforums', get_string('trackforums'), $choices);
$mform->setDefault('trackforums', core_user::get_property_default('trackforums'));
$choices = [
1 => get_string('markasreadonnotificationyes', 'mod_forum'),
0 => get_string('markasreadonnotificationno', 'mod_forum'),
];
$mform->addElement('select', 'markasreadonnotification', get_string('markasreadonnotification', 'mod_forum'), $choices);
$mform->addHelpButton('markasreadonnotification', 'markasreadonnotification', 'mod_forum');
$mform->disabledIf('markasreadonnotification', 'trackforums', 'eq', "0");
$mform->setDefault('markasreadonnotification', 1);
}
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'course', $COURSE->id);
$mform->setType('course', PARAM_INT);
$this->add_action_buttons(true, get_string('savechanges'));
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This function fetches group pictures from the data directory.
*
* Syntax: pix.php/groupid/f1.jpg or pix.php/groupid/f2.jpg
* OR: ?file=groupid/f1.jpg or ?file=groupid/f2.jpg
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
// Disable moodle specific debug messages and any errors in output.
define('NO_DEBUG_DISPLAY', true);
define('NO_MOODLE_COOKIES', true); // Session not used here.
require_once('../config.php');
require_once($CFG->libdir.'/filelib.php');
$relativepath = get_file_argument();
$args = explode('/', trim($relativepath, '/'));
if (count($args) == 2) {
$groupid = (integer)$args[0];
$image = $args[1];
$pathname = $CFG->dataroot.'/groups/'.$groupid.'/'.$image;
} else {
$image = 'f1.png';
$pathname = $CFG->dirroot.'/pix/g/f1.png';
}
if (file_exists($pathname) and !is_dir($pathname)) {
send_file($pathname, $image);
} else {
header('HTTP/1.0 404 not found');
throw new \moodle_exception('filenotfound', 'error'); // This is not displayed on IIS??
}
+308
View File
@@ -0,0 +1,308 @@
<?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/>.
/**
* Lists all the users within a given course.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->dirroot.'/user/lib.php');
require_once($CFG->dirroot.'/course/lib.php');
require_once($CFG->dirroot.'/notes/lib.php');
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->libdir.'/filelib.php');
require_once($CFG->dirroot.'/enrol/locallib.php');
use core_table\local\filter\filter;
use core_table\local\filter\integer_filter;
use core_table\local\filter\string_filter;
$participantsperpage = intval(get_config('moodlecourse', 'participantsperpage'));
define('DEFAULT_PAGE_SIZE', (!empty($participantsperpage) ? $participantsperpage : 20));
$page = optional_param('page', 0, PARAM_INT); // Which page to show.
$perpage = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT); // How many per page.
$contextid = optional_param('contextid', 0, PARAM_INT); // One of this or.
$courseid = optional_param('id', 0, PARAM_INT); // This are required.
$newcourse = optional_param('newcourse', false, PARAM_BOOL);
$roleid = optional_param('roleid', 0, PARAM_INT);
$urlgroupid = optional_param('group', 0, PARAM_INT);
$PAGE->set_url('/user/index.php', array(
'page' => $page,
'perpage' => $perpage,
'contextid' => $contextid,
'id' => $courseid,
'newcourse' => $newcourse));
if ($contextid) {
$context = context::instance_by_id($contextid, MUST_EXIST);
if ($context->contextlevel != CONTEXT_COURSE) {
throw new \moodle_exception('invalidcontext');
}
$course = $DB->get_record('course', array('id' => $context->instanceid), '*', MUST_EXIST);
} else {
$course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
$context = context_course::instance($course->id, MUST_EXIST);
}
// Not needed anymore.
unset($contextid);
unset($courseid);
require_login($course);
$systemcontext = context_system::instance();
$isfrontpage = ($course->id == SITEID);
$frontpagectx = context_course::instance(SITEID);
if ($isfrontpage) {
$PAGE->set_pagelayout('admin');
course_require_view_participants($systemcontext);
} else {
$PAGE->set_pagelayout('incourse');
course_require_view_participants($context);
}
// Trigger events.
user_list_view($course, $context);
$PAGE->set_title("$course->shortname: ".get_string('participants'));
$PAGE->set_heading($course->fullname);
$PAGE->set_pagetype('course-view-participants');
$PAGE->set_docs_path('enrol/users');
$PAGE->add_body_class('path-user'); // So we can style it independently.
$PAGE->set_other_editing_capability('moodle/course:manageactivities');
// Expand the users node in the settings navigation when it exists because those pages
// are related to this one.
$node = $PAGE->settingsnav->find('users', navigation_node::TYPE_CONTAINER);
if ($node) {
$node->force_open();
}
echo $OUTPUT->header();
$participanttable = new \core_user\table\participants("user-index-participants-{$course->id}");
// Manage enrolments.
$manager = new course_enrolment_manager($PAGE, $course);
$enrolbuttons = $manager->get_manual_enrol_buttons();
$enrolrenderer = $PAGE->get_renderer('core_enrol');
$enrolbuttonsout = '';
foreach ($enrolbuttons as $enrolbutton) {
$enrolbuttonsout .= $enrolrenderer->render($enrolbutton);
}
echo $OUTPUT->render_participants_tertiary_nav($course, html_writer::div($enrolbuttonsout, '', [
'data-region' => 'wrapper',
'data-table-uniqueid' => $participanttable->uniqueid,
]));
$filterset = new \core_user\table\participants_filterset();
$filterset->add_filter(new integer_filter('courseid', filter::JOINTYPE_DEFAULT, [(int)$course->id]));
$canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
$filtergroupids = $urlgroupid ? [$urlgroupid] : [];
// Force group filtering if user should only see a subset of groups' users.
if ($course->groupmode != NOGROUPS && !$canaccessallgroups) {
if ($filtergroupids) {
$filtergroupids = array_intersect(
$filtergroupids,
array_keys(groups_get_all_groups($course->id, $USER->id))
);
} else {
$filtergroupids = array_keys(groups_get_all_groups($course->id, $USER->id));
}
if (empty($filtergroupids)) {
if ($course->groupmode == SEPARATEGROUPS) {
// The user is not in a group so show message and exit.
echo $OUTPUT->notification(get_string('notingroup'));
echo $OUTPUT->footer();
exit();
} else {
$filtergroupids = [(int) groups_get_course_group($course, true)];
}
}
}
// Apply groups filter if included in URL or forced due to lack of capabilities.
if (!empty($filtergroupids)) {
$filterset->add_filter(new integer_filter('groups', filter::JOINTYPE_DEFAULT, $filtergroupids));
}
// Display single group information if requested in the URL.
if ($urlgroupid > 0 && ($course->groupmode != SEPARATEGROUPS || $canaccessallgroups)) {
$grouprenderer = $PAGE->get_renderer('core_group');
$groupdetailpage = new \core_group\output\group_details($urlgroupid);
echo $grouprenderer->group_details($groupdetailpage);
}
// Filter by role if passed via URL (used on profile page).
if ($roleid) {
$viewableroles = get_profile_roles($context);
// Apply filter if the user can view this role.
if (array_key_exists($roleid, $viewableroles)) {
$filterset->add_filter(new integer_filter('roles', filter::JOINTYPE_DEFAULT, [$roleid]));
}
}
// Render the user filters.
$userrenderer = $PAGE->get_renderer('core_user');
echo $userrenderer->participants_filter($context, $participanttable->uniqueid);
echo '<div class="userlist">';
// Do this so we can get the total number of rows.
ob_start();
$participanttable->set_filterset($filterset);
$participanttable->out($perpage, true);
$participanttablehtml = ob_get_contents();
ob_end_clean();
echo html_writer::start_tag('form', [
'action' => 'action_redir.php',
'method' => 'post',
'id' => 'participantsform',
'data-course-id' => $course->id,
'data-table-unique-id' => $participanttable->uniqueid,
]);
echo '<div>';
echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
echo '<input type="hidden" name="returnto" value="'.s($PAGE->url->out(false)).'" />';
echo html_writer::tag(
'p',
get_string('countparticipantsfound', 'core_user', $participanttable->totalrows),
[
'data-region' => 'participant-count',
]
);
echo $participanttablehtml;
$bulkoptions = (object) [
'uniqueid' => $participanttable->uniqueid,
];
echo '<br /><div class="buttons"><div class="d-flex flex-wrap align-items-center">';
echo html_writer::start_tag('div', array('class' => 'btn-group'));
if ($participanttable->get_page_size() < $participanttable->totalrows) {
// Select all users, refresh table showing all users and mark them all selected.
$label = get_string('selectalluserswithcount', 'moodle', $participanttable->totalrows);
echo html_writer::empty_tag('input', [
'type' => 'button',
'id' => 'checkall',
'class' => 'btn btn-secondary',
'value' => $label,
'data-target-page-size' => TABLE_SHOW_ALL_PAGE_SIZE,
]);
}
echo html_writer::end_tag('div');
$displaylist = array();
if (!empty($CFG->messaging) && has_all_capabilities(['moodle/site:sendmessage', 'moodle/course:bulkmessaging'], $context)) {
$displaylist['#messageselect'] = get_string('messageselectadd');
}
if (!empty($CFG->enablenotes) && has_capability('moodle/notes:manage', $context) && $context->id != $frontpagectx->id) {
$displaylist['#addgroupnote'] = get_string('addnewnote', 'notes');
}
$params = ['operation' => 'download_participants'];
$downloadoptions = [];
$formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
foreach ($formats as $format) {
if ($format->is_enabled()) {
$params = ['operation' => 'download_participants', 'dataformat' => $format->name];
$url = new moodle_url('bulkchange.php', $params);
$downloadoptions[$url->out(false)] = get_string('dataformat', $format->component);
}
}
if (!empty($downloadoptions)) {
$displaylist[] = [get_string('downloadas', 'table') => $downloadoptions];
}
if ($context->id != $frontpagectx->id) {
$instances = $manager->get_enrolment_instances();
$plugins = $manager->get_enrolment_plugins(false);
foreach ($instances as $key => $instance) {
if (!isset($plugins[$instance->enrol])) {
// Weird, some broken stuff in plugin.
continue;
}
$plugin = $plugins[$instance->enrol];
$bulkoperations = $plugin->get_bulk_operations($manager);
$pluginoptions = [];
foreach ($bulkoperations as $key => $bulkoperation) {
$params = ['plugin' => $plugin->get_name(), 'operation' => $key];
$url = new moodle_url('bulkchange.php', $params);
$pluginoptions[$url->out(false)] = $bulkoperation->get_title();
}
if (!empty($pluginoptions)) {
$name = get_string('pluginname', 'enrol_' . $plugin->get_name());
$displaylist[] = [$name => $pluginoptions];
}
}
}
$selectactionparams = array(
'id' => 'formactionid',
'class' => 'ml-2',
'data-action' => 'toggle',
'data-togglegroup' => 'participants-table',
'data-toggle' => 'action',
'disabled' => 'disabled'
);
$label = html_writer::tag('label', get_string("withselectedusers"),
['for' => 'formactionid', 'class' => 'col-form-label d-inline']);
$select = html_writer::select($displaylist, 'formaction', '', ['' => 'choosedots'], $selectactionparams);
echo html_writer::tag('div', $label . $select);
echo '<input type="hidden" name="id" value="' . $course->id . '" />';
echo '<div class="d-none" data-region="state-help-icon">' . $OUTPUT->help_icon('publishstate', 'notes') . '</div>';
echo '</div></div></div>';
$bulkoptions->noteStateNames = note_get_state_names();
echo '</form>';
$PAGE->requires->js_call_amd('core_user/participants', 'init', [$bulkoptions]);
echo '</div>'; // Userlist.
$enrolrenderer = $PAGE->get_renderer('core_enrol');
// Need to re-generate the buttons to avoid having elements with duplicate ids on the page.
$enrolbuttons = $manager->get_manual_enrol_buttons();
$enrolbuttonsout = '';
foreach ($enrolbuttons as $enrolbutton) {
$enrolbuttonsout .= $enrolrenderer->render($enrolbutton);
}
echo html_writer::div($enrolbuttonsout, 'd-flex justify-content-end', [
'data-region' => 'wrapper',
'data-table-uniqueid' => $participanttable->uniqueid,
]);
echo $OUTPUT->footer();
+83
View File
@@ -0,0 +1,83 @@
<?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/>.
/**
* Allows you to edit a users profile
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once('../config.php');
require_once($CFG->libdir.'/gdlib.php');
require_once($CFG->dirroot.'/user/language_form.php');
require_once($CFG->dirroot.'/user/editlib.php');
require_once($CFG->dirroot.'/user/lib.php');
$userid = optional_param('id', $USER->id, PARAM_INT); // User id.
$courseid = optional_param('course', SITEID, PARAM_INT); // Course id (defaults to Site).
$PAGE->set_url('/user/language.php', array('id' => $userid, 'course' => $courseid));
list($user, $course) = useredit_setup_preference_page($userid, $courseid);
// Create form.
$languageform = new user_edit_language_form(null, array('userid' => $user->id));
$languageform->set_data($user);
$redirect = new moodle_url("/user/preferences.php", array('userid' => $user->id));
if ($languageform->is_cancelled()) {
redirect($redirect);
} else if ($data = $languageform->get_data()) {
$lang = $data->lang;
// If the specified language does not exist, use the site default.
if (!get_string_manager()->translation_exists($lang, false)) {
$lang = core_user::get_property_default('lang');
}
$user->lang = $lang;
// Update user with new language.
user_update_user($user, false, false);
// Trigger event.
\core\event\user_updated::create_from_userid($user->id)->trigger();
if ($USER->id == $user->id) {
$USER->lang = $lang;
}
redirect($redirect, get_string('changessaved'), null, \core\output\notification::NOTIFY_SUCCESS);
}
// Display page header.
$streditmylanguage = get_string('preferredlanguage');
$userfullname = fullname($user, true);
$PAGE->navbar->includesettingsbase = true;
$PAGE->set_title("$course->shortname: $streditmylanguage");
$PAGE->set_heading($userfullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($streditmylanguage);
// Finally display THE form.
$languageform->display();
// And proper footer.
echo $OUTPUT->footer();
+91
View File
@@ -0,0 +1,91 @@
<?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/>.
/**
* Form to edit a users preferred language
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.'); // It must be included from a Moodle page.
}
require_once($CFG->dirroot.'/lib/formslib.php');
require_once($CFG->dirroot.'/user/lib.php');
/**
* Class user_edit_form.
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_edit_language_form extends moodleform {
/**
* Define the form.
*/
public function definition() {
global $CFG, $COURSE, $USER;
$mform = $this->_form;
$userid = $USER->id;
if (is_array($this->_customdata)) {
if (array_key_exists('userid', $this->_customdata)) {
$userid = $this->_customdata['userid'];
}
}
// Add some extra hidden fields.
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'course', $COURSE->id);
$mform->setType('course', PARAM_INT);
$purpose = user_edit_map_field_purpose($userid, 'lang');
$translations = get_string_manager()->get_list_of_translations();
$mform->addElement('select', 'lang', get_string('preferredlanguage'), $translations, $purpose);
$mform->setDefault('lang', core_user::get_property_default('lang'));
$this->add_action_buttons(true, get_string('savechanges'));
}
/**
* Extend the form definition after the data has been parsed.
*/
public function definition_after_data() {
global $CFG, $DB, $OUTPUT;
$mform = $this->_form;
// If language does not exist, use site default lang.
if ($langsel = $mform->getElementValue('lang')) {
$lang = reset($langsel);
// Check lang exists.
if (!get_string_manager()->translation_exists($lang, false)) {
$langel =& $mform->getElement('lang');
$langel->setValue(core_user::get_property_default('lang'));
}
}
}
}
+1433
View File
File diff suppressed because it is too large Load Diff
+142
View File
@@ -0,0 +1,142 @@
<?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 test client.
*
* @package core_webservice
* @copyright 2009 Moodle Pty Ltd (http://moodle.com)
* @author Jerome Mouneyrac
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require('../config.php');
require_login();
$usercontext = context_user::instance($USER->id);
$PAGE->set_context($usercontext);
$PAGE->set_url('/user/managetoken.php');
$PAGE->set_title(get_string('securitykeys', 'webservice'));
$PAGE->set_pagelayout('admin');
$rsstokenboxhtml = $webservicetokenboxhtml = '';
// Manage user web service tokens.
if ( !is_siteadmin($USER->id)
&& !empty($CFG->enablewebservices)
&& has_capability('moodle/webservice:createtoken', $usercontext )) {
require_once($CFG->dirroot.'/webservice/lib.php');
$action = optional_param('action', '', PARAM_ALPHANUMEXT);
$tokenid = optional_param('tokenid', '', PARAM_SAFEDIR);
$confirm = optional_param('confirm', 0, PARAM_BOOL);
$webservice = new webservice(); // Load the webservice library.
$wsrenderer = $PAGE->get_renderer('core', 'webservice');
if ($action == 'resetwstoken') {
$token = $webservice->get_created_by_user_ws_token($USER->id, $tokenid);
// Display confirmation page to Reset the token.
if (!$confirm) {
$resetconfirmation = $wsrenderer->user_reset_token_confirmation($token);
} else {
// Delete the token that need to be regenerated.
require_sesskey();
$webservice->delete_user_ws_token($tokenid);
// Now re-create one against the same service.
\core_external\util::generate_token(
EXTERNAL_TOKEN_PERMANENT,
\core_external\util::get_service_by_id($token->externalserviceid),
$USER->id,
context_system::instance()
);
redirect($PAGE->url, get_string('resettokencomplete', 'core_webservice'));
}
}
// No point creating the table is we're just displaying a confirmation screen.
if (empty($resetconfirmation)) {
$webservice->generate_user_ws_tokens($USER->id); // Generate all token that need to be generated.
$tokens = $webservice->get_user_ws_tokens($USER->id);
foreach ($tokens as $token) {
if ($token->restrictedusers) {
$authlist = $webservice->get_ws_authorised_user($token->wsid, $USER->id);
if (empty($authlist)) {
$token->enabled = false;
}
}
}
$webservicetokenboxhtml = $wsrenderer->user_webservice_tokens_box($tokens, $USER->id,
$CFG->enablewsdocumentation); // Display the box for web service token.
}
}
// RSS keys.
if (!empty($CFG->enablerssfeeds)) {
require_once($CFG->dirroot.'/lib/rsslib.php');
$action = optional_param('action', '', PARAM_ALPHANUMEXT);
$confirm = optional_param('confirm', 0, PARAM_BOOL);
$rssrenderer = $PAGE->get_renderer('core', 'rss');
if ($action == 'resetrsstoken') {
// Display confirmation page to Reset the token.
if (!$confirm) {
$resetconfirmation = $rssrenderer->user_reset_rss_token_confirmation();
} else {
require_sesskey();
rss_delete_token($USER->id);
redirect($PAGE->url, get_string('resettokencomplete', 'core_webservice'));
}
}
if (empty($resetconfirmation)) {
$token = rss_get_token($USER->id);
$rsstokenboxhtml = $rssrenderer->user_rss_token_box($token); // Display the box for the user's RSS token.
}
}
// PAGE OUTPUT.
echo $OUTPUT->header();
if (!empty($resetconfirmation)) {
echo $resetconfirmation;
} else {
if (!empty($SESSION->webservicenewlycreatedtoken)) {
$webservicemanager = new webservice();
$newtoken = $webservicemanager->get_created_by_user_ws_token(
$USER->id,
$SESSION->webservicenewlycreatedtoken
);
if ($newtoken) {
// Unset the session variable.
unset($SESSION->webservicenewlycreatedtoken);
// Display the newly created token.
echo $OUTPUT->render_from_template(
'core_admin/webservice_token_new', ['token' => $newtoken->token, 'tokenname' => $newtoken->tokenname]
);
}
}
echo $webservicetokenboxhtml;
echo $rsstokenboxhtml;
}
echo $OUTPUT->footer();
+51
View File
@@ -0,0 +1,51 @@
<?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/>.
/**
* BC user image location
*
* @package core_user
* @category files
* @copyright 2010 Petr Skoda (http://skodak.org)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('NO_DEBUG_DISPLAY', true);
define('NOMOODLECOOKIE', 1);
require('../config.php');
$PAGE->set_url('/user/pix.php');
$PAGE->set_context(null);
$relativepath = get_file_argument('pix.php');
$args = explode('/', trim($relativepath, '/'));
if (count($args) == 2) {
$userid = (integer)$args[0];
if ($args[1] === 'f1.jpg') {
$image = 'f1';
} else {
$image = 'f2';
}
if ($usercontext = context_user::instance($userid, IGNORE_MISSING)) {
$url = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', null, '/', $image);
redirect($url);
}
}
redirect($OUTPUT->image_url('u/f1'));
+99
View File
@@ -0,0 +1,99 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file is part of the User section Moodle
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
// Do not check for the site policies in require_login() to avoid the redirect loop.
define('NO_SITEPOLICY_CHECK', true);
require_once('../config.php');
require_once($CFG->libdir.'/filelib.php');
require_once($CFG->libdir.'/resourcelib.php');
$agree = optional_param('agree', 0, PARAM_BOOL);
$PAGE->set_url('/user/policy.php');
$PAGE->set_popup_notification_allowed(false);
if (!isloggedin()) {
require_login();
}
if (!empty($SESSION->wantsurl)) {
$return = $SESSION->wantsurl;
} else {
$return = $CFG->wwwroot.'/';
}
$sitepolicymanager = new \core_privacy\local\sitepolicy\manager();
if (!empty($CFG->sitepolicyhandler)) {
// We are on the wrong page, site policies are managed by somebody else.
if ($sitepolicyurl = $sitepolicymanager->get_redirect_url(isguestuser())) {
redirect($sitepolicyurl);
} else {
redirect($return);
}
}
$sitepolicy = $sitepolicymanager->get_embed_url(isguestuser());
if (empty($sitepolicy)) {
// Nothing to agree to, sorry, hopefully we will not get to infinite loop.
redirect($return);
}
if ($agree and confirm_sesskey()) { // User has agreed.
$sitepolicymanager->accept();
unset($SESSION->wantsurl);
redirect($return);
}
$strpolicyagree = get_string('policyagree');
$strpolicyagreement = get_string('policyagreement');
$strpolicyagreementclick = get_string('policyagreementclick');
$PAGE->set_context(context_system::instance());
$PAGE->set_title($strpolicyagreement);
$PAGE->set_heading($SITE->fullname);
$PAGE->navbar->add($strpolicyagreement);
echo $OUTPUT->header();
echo $OUTPUT->heading($strpolicyagreement);
$mimetype = mimeinfo('type', $sitepolicy);
if ($mimetype == 'document/unknown') {
// Fallback for missing index.php, index.html.
$mimetype = 'text/html';
}
// We can not use our popups here, because the url may be arbitrary, see MDL-9823.
$clicktoopen = '<a href="'.$sitepolicy.'" onclick="this.target=\'_blank\'">'.$strpolicyagreementclick.'</a>';
echo '<div class="noticebox">';
echo resourcelib_embed_general($sitepolicy, $strpolicyagreement, $clicktoopen, $mimetype);
echo '</div>';
$formcontinue = new single_button(new moodle_url('policy.php', array('agree' => 1)), get_string('yes'));
$formcancel = new single_button(new moodle_url($CFG->wwwroot.'/login/logout.php', array('agree' => 0)), get_string('no'));
echo $OUTPUT->confirm($strpolicyagree, $formcontinue, $formcancel);
echo $OUTPUT->footer();
+139
View File
@@ -0,0 +1,139 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file is part of the User section Moodle
*
* @copyright 1999 Martin Dougiamas http://dougiamas.com
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core_user
*/
require_once(__DIR__ . '/../config.php');
if (empty($CFG->enableportfolios)) {
throw new \moodle_exception('disabled', 'portfolio');
}
require_once($CFG->libdir . '/portfoliolib.php');
require_once($CFG->libdir . '/portfolio/forms.php');
$config = optional_param('config', 0, PARAM_INT);
$hide = optional_param('hide', 0, PARAM_INT);
$courseid = optional_param('courseid', SITEID, PARAM_INT);
$url = new moodle_url('/user/portfolio.php', array('courseid' => $courseid));
if ($config !== 0) {
$url->param('config', $config);
}
if (! $course = $DB->get_record("course", array("id" => $courseid))) {
throw new \moodle_exception('invalidcourseid');
}
$user = $USER;
$fullname = fullname($user);
$strportfolios = get_string('portfolios', 'portfolio');
$configstr = get_string('manageyourportfolios', 'portfolio');
$namestr = get_string('name');
$pluginstr = get_string('plugin', 'portfolio');
$baseurl = $CFG->wwwroot . '/user/portfolio.php';
$introstr = get_string('intro', 'portfolio');
$showhide = get_string('showhide', 'portfolio');
$display = true; // Set this to false in the conditions to stop processing.
require_login($course, false);
$PAGE->set_url($url);
$PAGE->set_context(context_user::instance($user->id));
$PAGE->set_title($configstr);
$PAGE->set_heading($fullname);
$PAGE->set_pagelayout('admin');
echo $OUTPUT->header();
$showroles = 1;
if (!empty($config)) {
navigation_node::override_active_url(new moodle_url('/user/portfolio.php', array('courseid' => $courseid)));
$instance = portfolio_instance($config);
$mform = new portfolio_user_form('', array('instance' => $instance, 'userid' => $user->id));
if ($mform->is_cancelled()) {
redirect($baseurl);
exit;
} else if ($fromform = $mform->get_data()) {
if (!confirm_sesskey()) {
throw new \moodle_exception('confirmsesskeybad', '', $baseurl);
}
// This branch is where you process validated data.
$instance->set_user_config($fromform, $USER->id);
core_plugin_manager::reset_caches();
redirect($baseurl, get_string('instancesaved', 'portfolio'), 3);
exit;
} else {
echo $OUTPUT->heading(get_string('configplugin', 'portfolio'));
echo $OUTPUT->box_start();
$mform->display();
echo $OUTPUT->box_end();
$display = false;
}
} else if (!empty($hide)) {
$instance = portfolio_instance($hide);
$instance->set_user_config(array('visible' => !$instance->get_user_config('visible', $USER->id)), $USER->id);
core_plugin_manager::reset_caches();
}
if ($display) {
echo $OUTPUT->heading($configstr);
echo $OUTPUT->box_start();
echo html_writer::tag('p', $introstr);
if (!$instances = portfolio_instances(true, false)) {
throw new \moodle_exception('noinstances', 'portfolio', $CFG->wwwroot . '/user/view.php');
}
$table = new html_table();
$table->head = array($namestr, $pluginstr, $showhide);
$table->data = array();
foreach ($instances as $i) {
// Contents of the actions (Show / hide) column.
$actions = '';
// Configure icon.
if ($i->has_user_config()) {
$configurl = new moodle_url($baseurl);
$configurl->param('config', $i->get('id'));
$actions .= html_writer::link($configurl, $OUTPUT->pix_icon('t/edit', get_string('configure', 'portfolio')));
}
// Hide/show icon.
$visible = $i->get_user_config('visible', $USER->id);
$visibilityaction = $visible ? 'hide' : 'show';
$showhideurl = new moodle_url($baseurl);
$showhideurl->param('hide', $i->get('id'));
$actions .= html_writer::link($showhideurl, $OUTPUT->pix_icon('t/' . $visibilityaction, get_string($visibilityaction)));
$table->data[] = array($i->get('name'), $i->get('plugin'), $actions);
}
echo html_writer::table($table);
echo $OUTPUT->box_end();
}
echo $OUTPUT->footer();

Some files were not shown because too many files have changed in this diff Show More