ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç install.php000064400000035672152265520040006741 0ustar00. /** * This file is executed right after the install.xml * * For more information, take a look to the documentation available: * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @package core_install * @category upgrade * @copyright 2009 Petr Skoda (http://skodak.org) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Main post-install tasks to be executed after the BD schema is available * * This function is automatically executed after Moodle core DB has been * created at initial install. It's in charge of perform the initial tasks * not covered by the {@link install.xml} file, like create initial users, * roles, templates, moving stuff from other plugins... * * Note that the function is only invoked once, at install time, so if new tasks * are needed in the future, they will need to be added both here (for new sites) * and in the corresponding {@link upgrade.php} file (for existing sites). * * All plugins within Moodle (modules, blocks, reports...) support the existence of * their own install.php file, using the "Frankenstyle" component name as * defined at {@link https://moodledev.io/general/development/policies/codingstyle/frankenstyle}, for example: * - {@link xmldb_page_install()}. (modules don't require the plugintype ("mod_") to be used. * - {@link xmldb_enrol_meta_install()}. * - {@link xmldb_workshopform_accumulative_install()}. * - .... * * Finally, note that it's also supported to have one uninstall.php file that is * executed also once, each time one plugin is uninstalled (before the DB schema is * deleted). Those uninstall files will contain one function, using the "Frankenstyle" * naming conventions, like {@link xmldb_enrol_meta_uninstall()} or {@link xmldb_workshop_uninstall()}. */ function xmldb_main_install() { global $CFG, $DB, $SITE, $OUTPUT; // Make sure system context exists $syscontext = context_system::instance(0, MUST_EXIST, false); if ($syscontext->id != SYSCONTEXTID) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new system context id!'); } // Create site course if ($DB->record_exists('course', array())) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create frontpage course, courses already exist.'); } $newsite = new stdClass(); $newsite->fullname = ''; $newsite->shortname = ''; $newsite->summary = NULL; $newsite->newsitems = 3; $newsite->numsections = 1; $newsite->category = 0; $newsite->format = 'site'; // Only for this course $newsite->timecreated = time(); $newsite->timemodified = $newsite->timecreated; if (defined('SITEID')) { $newsite->id = SITEID; $DB->import_record('course', $newsite); $DB->get_manager()->reset_sequence('course'); } else { $newsite->id = $DB->insert_record('course', $newsite); define('SITEID', $newsite->id); } // set the field 'numsections'. We can not use format_site::update_format_options() because // the file is not loaded $DB->insert_record('course_format_options', array('courseid' => SITEID, 'format' => 'site', 'sectionid' => 0, 'name' => 'numsections', 'value' => $newsite->numsections)); $SITE = get_site(); if ($newsite->id != $SITE->id) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new site course id!'); } // Make sure site course context exists context_course::instance($SITE->id); // Update the global frontpage cache $SITE = $DB->get_record('course', array('id'=>$newsite->id), '*', MUST_EXIST); // Create default course category if ($DB->record_exists('course_categories', array())) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default course category, categories already exist.'); } $cat = new stdClass(); $cat->name = get_string('defaultcategoryname'); $cat->descriptionformat = FORMAT_HTML; $cat->depth = 1; $cat->sortorder = get_max_courses_in_category(); $cat->timemodified = time(); $catid = $DB->insert_record('course_categories', $cat); $DB->set_field('course_categories', 'path', '/'.$catid, array('id'=>$catid)); // Make sure category context exists context_coursecat::instance($catid); $defaults = array( 'rolesactive' => '0', // marks fully set up system 'auth' => 'email', 'enrol_plugins_enabled' => 'manual,guest,self,cohort', 'theme' => theme_config::DEFAULT_THEME, 'filter_multilang_converted' => 1, 'siteidentifier' => random_string(32).get_host_from_url($CFG->wwwroot), 'backup_version' => 2008111700, 'backup_release' => '2.0 dev', 'mnet_dispatcher_mode' => 'off', 'sessiontimeout' => 8 * 60 * 60, // Must be present during roles installation. 'stringfilters' => '', // These two are managed in a strange way by the filters. 'filterall' => 0, // setting page, so have to be initialised here. 'texteditors' => 'tiny,textarea', 'antiviruses' => '', 'media_plugins_sortorder' => 'videojs,youtube', 'upgrade_extracreditweightsstepignored' => 1, // New installs should not run this upgrade step. 'upgrade_calculatedgradeitemsignored' => 1, // New installs should not run this upgrade step. 'upgrade_letterboundarycourses' => 1, // New installs should not run this upgrade step. 'format_plugins_sortorder' => 'topics,weeks,singleactivity,social', // Default order for course format plugins. ); foreach($defaults as $key => $value) { set_config($key, $value); } // Bootstrap mnet $mnethost = new stdClass(); $mnethost->wwwroot = $CFG->wwwroot; $mnethost->name = ''; $mnethost->name = ''; $mnethost->public_key = ''; if (empty($_SERVER['SERVER_ADDR'])) { // SERVER_ADDR is only returned by Apache-like webservers preg_match("@^(?:http[s]?://)?([A-Z0-9\-\.]+).*@i", $CFG->wwwroot, $matches); $my_hostname = $matches[1]; $my_ip = gethostbyname($my_hostname); // Returns unmodified hostname on failure. DOH! if ($my_ip == $my_hostname) { $mnethost->ip_address = 'UNKNOWN'; } else { $mnethost->ip_address = $my_ip; } } else { $mnethost->ip_address = $_SERVER['SERVER_ADDR']; } $mnetid = $DB->insert_record('mnet_host', $mnethost); set_config('mnet_localhost_id', $mnetid); // Initial insert of mnet applications info $mnet_app = new stdClass(); $mnet_app->name = 'moodle'; $mnet_app->display_name = 'Moodle'; $mnet_app->xmlrpc_server_url = '/mnet/xmlrpc/server.php'; $mnet_app->sso_land_url = '/auth/mnet/land.php'; $mnet_app->sso_jump_url = '/auth/mnet/jump.php'; $moodleapplicationid = $DB->insert_record('mnet_application', $mnet_app); $mnet_app = new stdClass(); $mnet_app->name = 'mahara'; $mnet_app->display_name = 'Mahara'; $mnet_app->xmlrpc_server_url = '/api/xmlrpc/server.php'; $mnet_app->sso_land_url = '/auth/xmlrpc/land.php'; $mnet_app->sso_jump_url = '/auth/xmlrpc/jump.php'; $DB->insert_record('mnet_application', $mnet_app); // Set up the probably-to-be-removed-soon 'All hosts' record $mnetallhosts = new stdClass(); $mnetallhosts->wwwroot = ''; $mnetallhosts->ip_address = ''; $mnetallhosts->public_key = ''; $mnetallhosts->public_key_expires = 0; $mnetallhosts->last_connect_time = 0; $mnetallhosts->last_log_id = 0; $mnetallhosts->deleted = 0; $mnetallhosts->name = 'All Hosts'; $mnetallhosts->applicationid = $moodleapplicationid; $mnetallhosts->id = $DB->insert_record('mnet_host', $mnetallhosts, true); set_config('mnet_all_hosts_id', $mnetallhosts->id); // Create guest record - do not assign any role, guest user gets the default guest role automatically on the fly if ($DB->record_exists('user', array())) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default users, users already exist.'); } $guest = new stdClass(); $guest->auth = 'manual'; $guest->username = 'guest'; $guest->password = hash_internal_user_password('guest'); $guest->firstname = get_string('guestuser'); $guest->lastname = ' '; $guest->email = 'root@localhost'; $guest->description = get_string('guestuserinfo'); $guest->mnethostid = $CFG->mnet_localhost_id; $guest->confirmed = 1; $guest->lang = $CFG->lang; $guest->timemodified= time(); $guest->id = $DB->insert_record('user', $guest); if ($guest->id != 1) { echo $OUTPUT->notification('Unexpected id generated for the Guest account. Your database configuration or clustering setup may not be fully supported', 'notifyproblem'); } // Store guest id set_config('siteguest', $guest->id); // Make sure user context exists context_user::instance($guest->id); // Now create admin user $admin = new stdClass(); $admin->auth = 'manual'; $admin->firstname = get_string('admin'); $admin->lastname = get_string('user'); $admin->username = 'admin'; $admin->password = 'adminsetuppending'; $admin->email = ''; $admin->confirmed = 1; $admin->mnethostid = $CFG->mnet_localhost_id; $admin->lang = $CFG->lang; $admin->maildisplay = 1; $admin->timemodified = time(); $admin->lastip = CLI_SCRIPT ? '0.0.0.0' : getremoteaddr(); // installation hijacking prevention $admin->id = $DB->insert_record('user', $admin); if ($admin->id != 2) { echo $OUTPUT->notification('Unexpected id generated for the Admin account. Your database configuration or clustering setup may not be fully supported', 'notifyproblem'); } if ($admin->id != ($guest->id + 1)) { echo $OUTPUT->notification('Nonconsecutive id generated for the Admin account. Your database configuration or clustering setup may not be fully supported.', 'notifyproblem'); } // Store list of admins set_config('siteadmins', $admin->id); // Make sure user context exists context_user::instance($admin->id); // Install the roles system. $managerrole = create_role('', 'manager', '', 'manager'); $coursecreatorrole = create_role('', 'coursecreator', '', 'coursecreator'); $editteacherrole = create_role('', 'editingteacher', '', 'editingteacher'); $noneditteacherrole = create_role('', 'teacher', '', 'teacher'); $studentrole = create_role('', 'student', '', 'student'); $guestrole = create_role('', 'guest', '', 'guest'); $userrole = create_role('', 'user', '', 'user'); $frontpagerole = create_role('', 'frontpage', '', 'frontpage'); // Now is the correct moment to install capabilities - after creation of legacy roles, but before assigning of roles update_capabilities('moodle'); // Default allow role matrices. foreach ($DB->get_records('role') as $role) { foreach (array('assign', 'override', 'switch', 'view') as $type) { $function = "core_role_set_{$type}_allowed"; $allows = get_default_role_archetype_allows($type, $role->archetype); foreach ($allows as $allowid) { $function($role->id, $allowid); } } } // Set up the context levels where you can assign each role. set_role_contextlevels($managerrole, get_default_contextlevels('manager')); set_role_contextlevels($coursecreatorrole, get_default_contextlevels('coursecreator')); set_role_contextlevels($editteacherrole, get_default_contextlevels('editingteacher')); set_role_contextlevels($noneditteacherrole, get_default_contextlevels('teacher')); set_role_contextlevels($studentrole, get_default_contextlevels('student')); set_role_contextlevels($guestrole, get_default_contextlevels('guest')); set_role_contextlevels($userrole, get_default_contextlevels('user')); // Init theme, JS and template revisions. set_config('themerev', time()); set_config('jsrev', time()); set_config('templaterev', time()); // No admin setting for this any more, GD is now required, remove in Moodle 2.6. set_config('gdversion', 2); // Install licenses require_once($CFG->libdir . '/licenselib.php'); license_manager::install_licenses(); // Init profile pages defaults if ($DB->record_exists('my_pages', array())) { throw new moodle_exception('generalexceptionmessage', 'error', '', 'Can not create default profile pages, records already exist.'); } $mypage = new stdClass(); $mypage->userid = NULL; $mypage->name = '__default'; $mypage->private = 0; $mypage->sortorder = 0; $DB->insert_record('my_pages', $mypage); $mypage->private = 1; $DB->insert_record('my_pages', $mypage); $mycoursespage = new stdClass(); $mycoursespage->userid = null; $mycoursespage->name = '__courses'; $mycoursespage->private = 0; $mycoursespage->sortorder = 0; $DB->insert_record('my_pages', $mycoursespage); // Set a sensible default sort order for the most-used question types. set_config('multichoice_sortorder', 1, 'question'); set_config('truefalse_sortorder', 2, 'question'); set_config('match_sortorder', 3, 'question'); set_config('shortanswer_sortorder', 4, 'question'); set_config('numerical_sortorder', 5, 'question'); set_config('essay_sortorder', 6, 'question'); require_once($CFG->libdir . '/db/upgradelib.php'); make_default_scale(); make_competence_scale(); require_once($CFG->dirroot . '/badges/upgradelib.php'); // Core install and upgrade related functions only for badges. badges_install_default_backpacks(); // Create default core site admin presets. require_once($CFG->dirroot . '/admin/presets/classes/helper.php'); \core_adminpresets\helper::create_default_presets(); } tasks.php000064400000030232152265520040006403 0ustar00. /** * Definition of core scheduled tasks. * * The handlers defined on this file are processed and registered into * the Moodle DB after any install or upgrade operation. All plugins * support this. * * @package core * @category task * @copyright 2013 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /* List of handlers */ $tasks = array( array( 'classname' => 'core\task\session_cleanup_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\delete_unconfirmed_users_task', 'blocking' => 0, 'minute' => '0', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\delete_incomplete_users_task', 'blocking' => 0, 'minute' => '5', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\backup_cleanup_task', 'blocking' => 0, 'minute' => '10', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\tag_cron_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '3', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\context_cleanup_task', 'blocking' => 0, 'minute' => '25', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\cache_cleanup_task', 'blocking' => 0, 'minute' => '30', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\messaging_cleanup_task', 'blocking' => 0, 'minute' => '35', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\send_new_user_passwords_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\send_failed_login_notifications_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\create_contexts_task', 'blocking' => 1, 'minute' => '0', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\grade_cron_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\grade_history_cleanup_task', 'blocking' => 0, 'minute' => '*', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\completion_regular_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\completion_daily_task', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\portfolio_cron_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\plagiarism_cron_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\calendar_cron_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\blog_cron_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\question_preview_cleanup_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\registration_cron_task', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '*', 'dayofweek' => 'R', 'month' => '*' ), array( 'classname' => 'core\task\check_for_updates_task', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\cache_cron_task', 'blocking' => 0, 'minute' => '50', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\automated_backup_task', 'blocking' => 0, 'minute' => '50', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\badges_cron_task', 'blocking' => 0, 'minute' => '*/5', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\badges_message_task', 'blocking' => 0, 'minute' => '*/5', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\file_temp_cleanup_task', 'blocking' => 0, 'minute' => '55', 'hour' => '*/6', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\file_trash_cleanup_task', 'blocking' => 0, 'minute' => '55', 'hour' => '*/6', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\search_index_task', 'blocking' => 0, 'minute' => '*/30', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\search_optimize_task', 'blocking' => 0, 'minute' => '15', 'hour' => '*/12', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\stats_cron_task', 'blocking' => 0, 'minute' => '0', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\password_reset_cleanup_task', 'blocking' => 0, 'minute' => '0', 'hour' => '*/6', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\complete_plans_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\sync_plans_from_template_cohorts_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core_files\task\conversion_cleanup_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '2', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\oauth2\refresh_system_tokens_task', 'blocking' => 0, 'minute' => '30', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\analytics_cleanup_task', 'blocking' => 0, 'minute' => '42', 'hour' => '*', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\task_log_cleanup_task', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\h5p_get_content_types_task', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '1', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\h5p_clean_orphaned_records_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ), array( 'classname' => 'core\task\antivirus_cleanup_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*', ), array( 'classname' => 'core_reportbuilder\task\send_schedules', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'month' => '*', 'dayofweek' => '*', ), [ 'classname' => 'core\task\task_lock_cleanup_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ], [ 'classname' => 'core_xapi\task\state_cleanup_task', 'blocking' => 0, 'minute' => 'R', 'hour' => '0', 'day' => '*', 'dayofweek' => '*', 'month' => '*' ], [ 'classname' => 'core\task\show_started_courses_task', 'blocking' => 0, 'minute' => '00', 'hour' => '01', 'day' => '*', 'dayofweek' => '*', 'month' => '*', 'disabled' => true, ], [ 'classname' => 'core\task\hide_ended_courses_task', 'blocking' => 0, 'minute' => '00', 'hour' => '01', 'day' => '*', 'dayofweek' => '*', 'month' => '*', 'disabled' => true, ], [ 'classname' => 'core_communication\task\synchronise_providers_task', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '*', 'dayofweek' => 'R', 'month' => '*', ], [ 'classname' => 'core\task\automated_backup_report_task', 'blocking' => 0, 'minute' => '*', 'hour' => '*', 'day' => '*', 'month' => '*', 'dayofweek' => '*', ], [ 'classname' => 'core\task\update_geoip2file_task', 'blocking' => 0, 'minute' => '0', 'hour' => 'R', 'day' => '*', 'month' => '*', 'dayofweek' => 'R', 'disabled' => true, ], [ 'classname' => 'core\task\stored_progress_bar_cleanup_task', 'blocking' => 0, 'minute' => '00', 'hour' => '01', 'day' => '*', 'dayofweek' => '*', 'month' => '*', 'disabled' => false, ], ); upgrade.php000064400000246503152265520040006717 0ustar00. /** * This file keeps track of upgrades to Moodle. * * Sometimes, changes between versions involve * alterations to database structures and other * major things that may break installations. * * The upgrade function in this file will attempt * to perform all the necessary actions to upgrade * your older installation to the current version. * * If there's something it cannot do itself, it * will tell you what you need to do. * * The commands in here will all be database-neutral, * using the methods of database_manager class * * Please do not forget to use upgrade_set_timeout() * before any action that may take longer time to finish. * * @package core_install * @category upgrade * @copyright 2006 onwards Martin Dougiamas http://dougiamas.com * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Main upgrade tasks to be executed on Moodle version bump * * This function is automatically executed after one bump in the Moodle core * version is detected. It's in charge of performing the required tasks * to raise core from the previous version to the next one. * * It's a collection of ordered blocks of code, named "upgrade steps", * each one performing one isolated (from the rest of steps) task. Usually * tasks involve creating new DB objects or performing manipulation of the * information for cleanup/fixup purposes. * * Each upgrade step has a fixed structure, that can be summarised as follows: * * if ($oldversion < XXXXXXXXXX.XX) { * // Explanation of the update step, linking to issue in the Tracker if necessary * upgrade_set_timeout(XX); // Optional for big tasks * // Code to execute goes here, usually the XMLDB Editor will * // help you here. See {@link https://moodledev.io/general/development/tools/xmldb}. * upgrade_main_savepoint(true, XXXXXXXXXX.XX); * } * * All plugins within Moodle (modules, blocks, reports...) support the existence of * their own upgrade.php file, using the "Frankenstyle" component name as * defined at {@link https://moodledev.io/general/development/policies/codingstyle/frankenstyle}, for example: * - {@see xmldb_page_upgrade($oldversion)}. (modules don't require the plugintype ("mod_") to be used. * - {@see xmldb_auth_manual_upgrade($oldversion)}. * - {@see xmldb_workshopform_accumulative_upgrade($oldversion)}. * - .... * * In order to keep the contents of this file reduced, it's allowed to create some helper * functions to be used here in the {@see upgradelib.php} file at the same directory. Note * that such a file must be manually included from upgrade.php, and there are some restrictions * about what can be used within it. * * For more information, take a look to the documentation available: * - Data definition API: {@link https://moodledev.io/docs/apis/core/dml/ddl} * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @param int $oldversion * @return bool always true */ function xmldb_main_upgrade($oldversion) { global $CFG, $DB; require_once($CFG->libdir . '/db/upgradelib.php'); // Core Upgrade-related functions. $dbman = $DB->get_manager(); // Loads ddl manager and xmldb classes. // Always keep this upgrade step with version being the minimum // allowed version to upgrade from (v4.1.2 right now). if ($oldversion < 2022112802) { // Just in case somebody hacks upgrade scripts or env, we really can not continue. echo("You need to upgrade to 4.1.2 or higher first!\n"); exit(1); // Note this savepoint is 100% unreachable, but needed to pass the upgrade checks. upgrade_main_savepoint(true, 2022112802); } // Automatically generated Moodle v4.2.0 release upgrade line. // Put any upgrade step following this. if ($oldversion < 2023051500.00) { // Define communication table. $table = new xmldb_table('communication'); // Adding fields to table communication. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE); $table->add_field('instanceid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id'); $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'instanceid'); $table->add_field('instancetype', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'component'); $table->add_field('provider', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'instancerype'); $table->add_field('roomname', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'provider'); $table->add_field('avatarfilename', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'roomname'); $table->add_field('active', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 1, 'avatarfilename'); // Add key. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for communication. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Define communication user table. $table = new xmldb_table('communication_user'); // Adding fields to table communication. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE); $table->add_field('commid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'id'); $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null, 'commid'); $table->add_field('synced', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 0, 'userid'); $table->add_field('deleted', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 0, 'synced'); // Add keys. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); $table->add_key('commid', XMLDB_KEY_FOREIGN, ['commid'], 'communication', ['id']); $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Conditionally launch create table for communication. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Main savepoint reached. upgrade_main_savepoint(true, 2023051500.00); } if ($oldversion < 2023062200.00) { // Remove device specific fields for themes from config table. unset_config('thememobile'); unset_config('themelegacy'); unset_config('themetablet'); upgrade_main_savepoint(true, 2023062200.00); } if ($oldversion < 2023062700.01) { // Define field name to be added to external_tokens. $table = new xmldb_table('external_tokens'); $field = new xmldb_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'lastaccess'); // Conditionally launch add field name. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Update the old external tokens. $sql = 'UPDATE {external_tokens} SET name = ' . $DB->sql_concat( // We only need the prefix, so leave the third param with an empty string. "'" . get_string('tokennameprefix', 'webservice', '') . "'", "id" ); $DB->execute($sql); // Main savepoint reached. upgrade_main_savepoint(true, 2023062700.01); } if ($oldversion < 2023062900.01) { // Define field avatarsynced to be added to communication. $table = new xmldb_table('communication'); $field = new xmldb_field('avatarsynced', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, 0, 'active'); // Conditionally launch add field avatarsynced. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2023062900.01); } if ($oldversion < 2023080100.00) { // Upgrade yaml mime type for existing yaml and yml files. $filetypes = [ '%.yaml' => 'application/yaml', '%.yml' => 'application/yaml,', ]; $select = $DB->sql_like('filename', '?', false); foreach ($filetypes as $extension => $mimetype) { $DB->set_field_select( 'files', 'mimetype', $mimetype, $select, [$extension] ); } // Main savepoint reached. upgrade_main_savepoint(true, 2023080100.00); } if ($oldversion < 2023081500.00) { upgrade_core_licenses(); upgrade_main_savepoint(true, 2023081500.00); } if ($oldversion < 2023081800.01) { // Remove enabledevicedetection and devicedetectregex from config table. unset_config('enabledevicedetection'); unset_config('devicedetectregex'); // Main savepoint reached. upgrade_main_savepoint(true, 2023081800.01); } if ($oldversion < 2023082200.01) { // Some MIME icons have been removed and replaced with existing icons. They need to be upgraded for custom MIME types. $replacedicons = [ 'avi' => 'video', 'base' => 'database', 'bmp' => 'image', 'html' => 'markup', 'jpeg' => 'image', 'mov' => 'video', 'mp3' => 'audio', 'mpeg' => 'video', 'png' => 'image', 'quicktime' => 'video', 'tiff' => 'image', 'wav' => 'audio', 'wmv' => 'video', ]; $custom = []; if (!empty($CFG->customfiletypes)) { if (array_key_exists('customfiletypes', $CFG->config_php_settings)) { // It's set in config.php, so the MIME icons can't be upgraded automatically. echo("\nYou need to manually check customfiletypes in config.php because some MIME icons have been removed!\n"); } else { // It's a JSON string in the config table. $custom = json_decode($CFG->customfiletypes); } } $changed = false; foreach ($custom as $customentry) { if (!empty($customentry->icon) && array_key_exists($customentry->icon, $replacedicons)) { $customentry->icon = $replacedicons[$customentry->icon]; $changed = true; } } if ($changed) { // Save the new customfiletypes. set_config('customfiletypes', json_encode($custom)); } // Main savepoint reached. upgrade_main_savepoint(true, 2023082200.01); } if ($oldversion < 2023082200.02) { // Some MIME icons have been removed. They need to be replaced to 'unknown' for custom MIME types. $removedicons = array_flip([ 'clip-353', 'edit', 'env', 'explore', 'folder-open', 'help', 'move', 'parent', ]); $custom = []; if (!empty($CFG->customfiletypes)) { if (array_key_exists('customfiletypes', $CFG->config_php_settings)) { // It's set in config.php, so the MIME icons can't be upgraded automatically. echo("\nYou need to manually check customfiletypes in config.php because some MIME icons have been removed!\n"); } else { // It's a JSON string in the config table. $custom = json_decode($CFG->customfiletypes); } } $changed = false; foreach ($custom as $customentry) { if (!empty($customentry->icon) && array_key_exists($customentry->icon, $removedicons)) { // The icon has been removed, so set it to unknown. $customentry->icon = 'unknown'; $changed = true; } } if ($changed) { // Save the new customfiletypes. set_config('customfiletypes', json_encode($custom)); } // Main savepoint reached. upgrade_main_savepoint(true, 2023082200.02); } if ($oldversion < 2023082200.04) { // Remove any non-unique filters/conditions. $duplicates = $DB->get_records_sql(" SELECT MIN(id) AS id, reportid, uniqueidentifier, iscondition FROM {reportbuilder_filter} GROUP BY reportid, uniqueidentifier, iscondition HAVING COUNT(*) > 1"); foreach ($duplicates as $duplicate) { $DB->delete_records_select( 'reportbuilder_filter', 'id <> :id AND reportid = :reportid AND uniqueidentifier = :uniqueidentifier AND iscondition = :iscondition', (array) $duplicate ); } // Define index report-filter (unique) to be added to reportbuilder_filter. $table = new xmldb_table('reportbuilder_filter'); $index = new xmldb_index('report-filter', XMLDB_INDEX_UNIQUE, ['reportid', 'uniqueidentifier', 'iscondition']); // Conditionally launch add index report-filter. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2023082200.04); } if ($oldversion < 2023082600.02) { // Get all the ids of users who still have md5 hashed passwords. if ($DB->sql_regex_supported()) { // If the database supports regex, we can add an exact check for md5. $condition = 'password ' . $DB->sql_regex() . ' :pattern'; $params = ['pattern' => "^[a-fA-F0-9]{32}$"]; } else { // Otherwise, we need to use a NOT LIKE condition and rule out bcrypt. $condition = $DB->sql_like('password', ':pattern', true, false, true); $params = ['pattern' => '$2y$%']; } // Regardless of database regex support we check the hash length which should be enough. // But extra regex or like matching makes sure. $sql = "SELECT id FROM {user} WHERE " . $DB->sql_length('password') . " = 32 AND $condition"; $userids = $DB->get_fieldset_sql($sql, $params); // Update the password for each user with a new SHA-512 hash. // Users won't know this password, but they can reset it. This is a security measure, // in case the database is compromised or the hash has been leaked elsewhere. foreach ($userids as $userid) { $password = base64_encode(random_bytes(24)); // Generate a new password for the user. $user = new \stdClass(); $user->id = $userid; $user->password = hash_internal_user_password($password); $DB->update_record('user', $user, true); } // Main savepoint reached. upgrade_main_savepoint(true, 2023082600.02); } if ($oldversion < 2023082600.03) { // The previous default configuration had a typo, check for its presence and correct if necessary. $sensiblesettings = get_config('adminpresets', 'sensiblesettings'); if (strpos($sensiblesettings, 'smtppass@none') !== false) { $newsensiblesettings = str_replace('smtppass@none', 'smtppass@@none', $sensiblesettings); set_config('sensiblesettings', $newsensiblesettings, 'adminpresets'); } // Main savepoint reached. upgrade_main_savepoint(true, 2023082600.03); } if ($oldversion < 2023082600.05) { unset_config('completiondefault'); // Main savepoint reached. upgrade_main_savepoint(true, 2023082600.05); } if ($oldversion < 2023090100.00) { // Upgrade MIME type for existing PSD files. $DB->set_field_select( 'files', 'mimetype', 'image/vnd.adobe.photoshop', $DB->sql_like('filename', '?', false), ['%.psd'] ); // Main savepoint reached. upgrade_main_savepoint(true, 2023090100.00); } if ($oldversion < 2023090200.01) { // Define table moodlenet_share_progress to be created. $table = new xmldb_table('moodlenet_share_progress'); // Adding fields to table moodlenet_share_progress. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('type', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, null); $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('cmid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('resourceurl', XMLDB_TYPE_CHAR, '255', null, null, null, null); $table->add_field('status', XMLDB_TYPE_INTEGER, '2', null, null, null, null); // Adding keys to table moodlenet_share_progress. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for moodlenet_share_progress. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Main savepoint reached. upgrade_main_savepoint(true, 2023090200.01); } if ($oldversion < 2023091300.03) { // Delete all the searchanywhere prefs in user_preferences table. $DB->delete_records('user_preferences', ['name' => 'userselector_searchanywhere']); // Main savepoint reached. upgrade_main_savepoint(true, 2023091300.03); } if ($oldversion < 2023100400.01) { // Delete datakey with datavalue -1. $DB->delete_records('messageinbound_datakeys', ['datavalue' => '-1']); // Main savepoint reached. upgrade_main_savepoint(true, 2023100400.01); } if ($oldversion < 2023100400.03) { // Define field id to be added to communication. $table = new xmldb_table('communication'); // Add the field and allow it to be nullable. // We need to backfill data before setting it to NOT NULL. $field = new xmldb_field( name: 'contextid', type: XMLDB_TYPE_INTEGER, precision: '10', notnull: null, previous: 'id', ); // Conditionally launch add field id. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Fill the existing data. $sql = <<get_recordset_sql( sql: $sql, params: [ 'contextcourse' => CONTEXT_COURSE, 'instancetype' => 'coursecommunication', ], ); foreach ($rs as $comm) { $DB->set_field( table: 'communication', newfield: 'contextid', newvalue: $comm->contextid, conditions: [ 'id' => $comm->id, ], ); } $rs->close(); $systemcontext = \core\context\system::instance(); $DB->set_field_select( table: 'communication', newfield: 'contextid', newvalue: $systemcontext->id, select: 'contextid IS NULL', ); // Now make it NOTNULL. $field = new xmldb_field( name: 'contextid', type: XMLDB_TYPE_INTEGER, precision: '10', notnull: XMLDB_NOTNULL, ); $dbman->change_field_notnull($table, $field); // Add the contextid constraint. $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']); $dbman->add_key($table, $key); // Main savepoint reached. upgrade_main_savepoint(true, 2023100400.03); } // Automatically generated Moodle v4.3.0 release upgrade line. // Put any upgrade step following this. if ($oldversion < 2023110900.00) { // Reorder the editors to make Tiny the default for all upgrades. $editors = []; array_push($editors, 'tiny'); $list = explode(',', $CFG->texteditors); foreach ($list as $editor) { if ($editor != 'tiny') { array_push($editors, $editor); } } set_config('texteditors', implode(',', $editors)); // Main savepoint reached. upgrade_main_savepoint(true, 2023110900.00); } if ($oldversion < 2023120100.01) { // The $CFG->linkcoursesections setting has been removed because it's not required anymore. // From now, sections will be always linked because a new page, section.php, has been created to display a single section. unset_config('linkcoursesections'); upgrade_main_savepoint(true, 2023120100.01); } if ($oldversion < 2023121800.02) { // Define field attemptsavailable to be added to task_adhoc. $table = new xmldb_table('task_adhoc'); $field = new xmldb_field( name: 'attemptsavailable', type: XMLDB_TYPE_INTEGER, precision: '2', unsigned: null, notnull: null, sequence: null, default: null, previous: 'pid', ); // Conditionally launch add field attemptsavailable. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Set attemptsavailable to 0 for the tasks that have not been run before. // Set attemptsavailable to 1 for the tasks that have been run and failed before. $DB->execute(' UPDATE {task_adhoc} SET attemptsavailable = CASE WHEN faildelay = 0 THEN 1 WHEN faildelay > 0 THEN 0 END '); // Main savepoint reached. upgrade_main_savepoint(true, 2023121800.02); } if ($oldversion < 2023122100.01) { // Define field component to be added to course_sections. $table = new xmldb_table('course_sections'); $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'availability'); // Conditionally launch add field component. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Define field itemid to be added to course_sections. $field = new xmldb_field('itemid', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'component'); // Conditionally launch add field itemid. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2023122100.01); } if ($oldversion < 2023122100.02) { $sqllike = $DB->sql_like('filtercondition', '?'); $params[] = '%includesubcategories%'; $sql = "SELECT qsr.* FROM {question_set_references} qsr WHERE $sqllike"; $results = $DB->get_recordset_sql($sql, $params); foreach ($results as $result) { $filtercondition = json_decode($result->filtercondition); if (isset($filtercondition->filter->category->includesubcategories)) { $filtercondition->filter->category->filteroptions = ['includesubcategories' => $filtercondition->filter->category->includesubcategories]; unset($filtercondition->filter->category->includesubcategories); $result->filtercondition = json_encode($filtercondition); $DB->update_record('question_set_references', $result); } } $results->close(); upgrade_main_savepoint(true, 2023122100.02); } if ($oldversion < 2024010400.01) { // Define index timecreated (not unique) to be added to notifications. $table = new xmldb_table('notifications'); $createdindex = new xmldb_index('timecreated', XMLDB_INDEX_NOTUNIQUE, ['timecreated']); // Conditionally launch add index timecreated. if (!$dbman->index_exists($table, $createdindex)) { $dbman->add_index($table, $createdindex); } // Define index timeread (not unique) to be added to notifications. $readindex = new xmldb_index('timeread', XMLDB_INDEX_NOTUNIQUE, ['timeread']); // Conditionally launch add index timeread. if (!$dbman->index_exists($table, $readindex)) { $dbman->add_index($table, $readindex); } // Main savepoint reached. upgrade_main_savepoint(true, 2024010400.01); } if ($oldversion < 2024012300.00) { // Define field valuetrust to be added to customfield_data. $table = new xmldb_table('customfield_data'); $field = new xmldb_field('valuetrust', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0', 'valueformat'); // Conditionally launch add field valuetrust. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2024012300.00); } if ($oldversion < 2024020200.01) { // If h5plib_v124 is no longer present, remove it. if (!file_exists($CFG->dirroot . '/h5p/h5plib/v124/version.php')) { // Clean config. uninstall_plugin('h5plib', 'v124'); } // If h5plib_v126 is present, set it as the default one. if (file_exists($CFG->dirroot . '/h5p/h5plib/v126/version.php')) { set_config('h5plibraryhandler', 'h5plib_v126'); } upgrade_main_savepoint(true, 2024020200.01); } if ($oldversion < 2024021500.01) { // Change default course formats order for sites never changed the default order. if (!get_config('core', 'format_plugins_sortorder')) { set_config('format_plugins_sortorder', 'topics,weeks,singleactivity,social'); } // Main savepoint reached. upgrade_main_savepoint(true, 2024021500.01); } if ($oldversion < 2024021500.02) { // A [name => url] map of new OIDC endpoints to be updated/created. $endpointuris = [ 'authorization_endpoint' => 'https://clever.com/oauth/authorize', 'token_endpoint' => 'https://clever.com/oauth/tokens', 'userinfo_endpoint' => 'https://api.clever.com/userinfo', 'jwks_uri' => 'https://clever.com/oauth/certs', ]; // A [internalfield => externalfield] map of new OIDC-based user field mappings to be updated/created. $userfieldmappings = [ 'idnumber' => 'sub', 'firstname' => 'given_name', 'lastname' => 'family_name', 'email' => 'email', ]; $admin = get_admin(); $adminid = $admin ? $admin->id : '0'; $cleverservices = $DB->get_records('oauth2_issuer', ['servicetype' => 'clever']); foreach ($cleverservices as $cleverservice) { $time = time(); // Insert/update the new endpoints. foreach ($endpointuris as $endpointname => $endpointuri) { $endpoint = ['issuerid' => $cleverservice->id, 'name' => $endpointname]; $endpointid = $DB->get_field('oauth2_endpoint', 'id', $endpoint); if ($endpointid) { $endpoint = array_merge($endpoint, [ 'id' => $endpointid, 'url' => $endpointuri, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->update_record('oauth2_endpoint', $endpoint); } else { $endpoint = array_merge($endpoint, [ 'url' => $endpointuri, 'timecreated' => $time, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->insert_record('oauth2_endpoint', $endpoint); } } // Insert/update new user field mappings. foreach ($userfieldmappings as $internalfieldname => $externalfieldname) { $fieldmap = ['issuerid' => $cleverservice->id, 'internalfield' => $internalfieldname]; $fieldmapid = $DB->get_field('oauth2_user_field_mapping', 'id', $fieldmap); if ($fieldmapid) { $fieldmap = array_merge($fieldmap, [ 'id' => $fieldmapid, 'externalfield' => $externalfieldname, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->update_record('oauth2_user_field_mapping', $fieldmap); } else { $fieldmap = array_merge($fieldmap, [ 'externalfield' => $externalfieldname, 'timecreated' => $time, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->insert_record('oauth2_user_field_mapping', $fieldmap); } } // Update the baseurl for the issuer. $cleverservice->baseurl = 'https://clever.com'; $cleverservice->timemodified = $time; $cleverservice->usermodified = $adminid; $DB->update_record('oauth2_issuer', $cleverservice); } upgrade_main_savepoint(true, 2024021500.02); } if ($oldversion < 2024022300.02) { // Removed advanced grade item settings. unset_config('grade_item_advanced'); upgrade_main_savepoint(true, 2024022300.02); } if ($oldversion < 2024030500.01) { // Define field firststartingtime to be added to task_adhoc. $table = new xmldb_table('task_adhoc'); $field = new xmldb_field('firststartingtime', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'attemptsavailable'); // Conditionally launch add field firststartingtime. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); // Main savepoint reached. upgrade_main_savepoint(true, 2024030500.01); } } if ($oldversion < 2024030500.02) { // Get all "select" custom field shortnames. $fieldshortnames = $DB->get_fieldset('customfield_field', 'shortname', ['type' => 'select']); // Ensure any used in custom reports columns are not using integer type aggregation. foreach ($fieldshortnames as $fieldshortname) { $DB->execute(" UPDATE {reportbuilder_column} SET aggregation = NULL WHERE " . $DB->sql_like('uniqueidentifier', ':uniqueidentifier', false) . " AND aggregation IN ('avg', 'max', 'min', 'sum') ", [ 'uniqueidentifier' => '%' . $DB->sql_like_escape(":customfield_{$fieldshortname}"), ]); } // Main savepoint reached. upgrade_main_savepoint(true, 2024030500.02); } if ($oldversion < 2024032600.01) { // Changing precision of field attemptsavailable on table task_adhoc to (2). $table = new xmldb_table('task_adhoc'); $field = new xmldb_field('attemptsavailable', XMLDB_TYPE_INTEGER, '2', null, null, null, null, 'pid'); // Launch change of precision for field. if (!$dbman->field_exists($table, $field)) { $dbman->change_field_precision($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2024032600.01); } if ($oldversion < 2024041200.00) { // Define field blocking to be dropped from task_adhoc. $table = new xmldb_table('task_adhoc'); $field = new xmldb_field('blocking'); // Conditionally launch drop field blocking. if ($dbman->field_exists($table, $field)) { $dbman->drop_field($table, $field); } // Define field blocking to be dropped from task_scheduled. $table = new xmldb_table('task_scheduled'); $field = new xmldb_field('blocking'); // Conditionally launch drop field blocking. if ($dbman->field_exists($table, $field)) { $dbman->drop_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2024041200.00); } // Automatically generated Moodle v4.4.0 release upgrade line. // Put any upgrade step following this. if ($oldversion < 2024070500.01) { // Remove the site_contactable config of the hub plugin from config plugin table. unset_config('site_contactable', 'hub'); // Main savepoint reached. upgrade_main_savepoint(true, 2024070500.01); } if ($oldversion < 2024071900.01) { // Define table stored_progress to be created. $table = new xmldb_table('stored_progress'); // Adding fields to table stored_progress. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('idnumber', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_field('timestart', XMLDB_TYPE_INTEGER, '20', null, null, null, null); $table->add_field('lastupdate', XMLDB_TYPE_INTEGER, '20', null, null, null, null); $table->add_field('percentcompleted', XMLDB_TYPE_NUMBER, '5, 2', null, null, null, '0'); $table->add_field('message', XMLDB_TYPE_CHAR, '255', null, null, null, null); $table->add_field('haserrored', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); // Adding keys to table stored_progress. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Adding indexes to table stored_progress. $table->add_index('uid_index', XMLDB_INDEX_NOTUNIQUE, ['idnumber']); // Conditionally launch create table for stored_progress. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Main savepoint reached. upgrade_main_savepoint(true, 2024071900.01); } if ($oldversion < 2024072600.01) { // If tool_innodb is no longer present, remove it. if (!file_exists($CFG->dirroot . '/admin/tool/innodb/version.php')) { // Delete tool_innodb. uninstall_plugin('tool', 'innodb'); } // Main savepoint reached. upgrade_main_savepoint(true, 2024072600.01); } if ($oldversion < 2024080500.00) { // Fix missing default admin presets "sensible settings" (those that should be treated as sensitive). $newsensiblesettings = [ 'bigbluebuttonbn_shared_secret@@none', 'apikey@@tiny_premium', 'matrixaccesstoken@@communication_matrix', 'api_secret@@factor_sms', ]; $sensiblesettings = get_config('adminpresets', 'sensiblesettings'); foreach ($newsensiblesettings as $newsensiblesetting) { if (strpos($sensiblesettings, $newsensiblesetting) === false) { $sensiblesettings .= ", {$newsensiblesetting}"; } } set_config('sensiblesettings', $sensiblesettings, 'adminpresets'); // Main savepoint reached. upgrade_main_savepoint(true, 2024080500.00); } if ($oldversion < 2024082900.01) { // If filter_tidy is no longer present, remove it. if (!file_exists($CFG->dirroot . '/filter/tidy/version.php')) { // Clean config. uninstall_plugin('filter', 'tidy'); } upgrade_main_savepoint(true, 2024082900.01); } if ($oldversion < 2024091000.01) { // Define table ai_policy_register to be created. $table = new xmldb_table('ai_policy_register'); // Adding fields to table ai_policy_register. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('timeaccepted', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); // Adding keys to table ai_policy_register. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); $table->add_key('userid', XMLDB_KEY_FOREIGN_UNIQUE, ['userid'], 'user', ['id']); // Conditionally launch create table for ai_policy_register. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Define table ai_action_generate_image to be created. $table = new xmldb_table('ai_action_generate_image'); // Adding fields to table ai_action_generate_image. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('prompt', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('numberimages', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('quality', XMLDB_TYPE_CHAR, '21', null, XMLDB_NOTNULL, null, null); $table->add_field('aspectratio', XMLDB_TYPE_CHAR, '20', null, null, null, null); $table->add_field('style', XMLDB_TYPE_CHAR, '20', null, null, null, null); $table->add_field('sourceurl', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('revisedprompt', XMLDB_TYPE_TEXT, null, null, null, null, null); // Adding keys to table ai_action_generate_image. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for ai_action_generate_image. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Define table ai_action_register to be created. $table = new xmldb_table('ai_action_register'); // Adding fields to table ai_action_register. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('actionname', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); $table->add_field('actionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('success', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0'); $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('provider', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); $table->add_field('errorcode', XMLDB_TYPE_INTEGER, '4', null, null, null, null); $table->add_field('errormessage', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); $table->add_field('timecompleted', XMLDB_TYPE_INTEGER, '10', null, null, null, null); // Adding keys to table ai_action_register. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); $table->add_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Adding indexes to table ai_action_register. $table->add_index('action', XMLDB_INDEX_UNIQUE, ['actionname', 'actionid']); $table->add_index('provider', XMLDB_INDEX_NOTUNIQUE, ['actionname', 'provider']); // Conditionally launch create table for ai_action_register. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Define table ai_action_generate_text to be created. $table = new xmldb_table('ai_action_generate_text'); // Adding fields to table ai_action_generate_text. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('prompt', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('responseid', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('fingerprint', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('generatedcontent', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('finishreason', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('prompttokens', XMLDB_TYPE_INTEGER, '10', null, null, null, null); $table->add_field('completiontoken', XMLDB_TYPE_INTEGER, '10', null, null, null, null); // Adding keys to table ai_action_generate_text. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for ai_action_generate_text. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Define table ai_action_summarise_text to be created. $table = new xmldb_table('ai_action_summarise_text'); // Adding fields to table ai_action_summarise_text. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('prompt', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('responseid', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('fingerprint', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('generatedcontent', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('finishreason', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('prompttokens', XMLDB_TYPE_INTEGER, '10', null, null, null, null); $table->add_field('completiontoken', XMLDB_TYPE_INTEGER, '10', null, null, null, null); // Adding keys to table ai_action_summarise_text. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for ai_action_summarise_text. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Main savepoint reached. upgrade_main_savepoint(true, 2024091000.01); } if ($oldversion < 2024091700.01) { // Convert the ai_action_register.success column to an integer, if necessary. upgrade_change_binary_column_to_int('ai_action_register', 'success', XMLDB_NOTNULL, 'actionid'); // Main savepoint reached. upgrade_main_savepoint(true, 2024091700.01); } if ($oldversion < 2024092000.01) { // Define table sms_messages to be created. $table = new xmldb_table('sms_messages'); // Adding fields to table sms_messages. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('recipientnumber', XMLDB_TYPE_CHAR, '30', null, XMLDB_NOTNULL, null, null); $table->add_field('content', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); $table->add_field('messagetype', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null); $table->add_field('recipientuserid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); $table->add_field('issensitive', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '0'); $table->add_field('gatewayid', XMLDB_TYPE_INTEGER, '10', null, null, null, null); $table->add_field('status', XMLDB_TYPE_CHAR, '100', null, null, null, null); $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null); // Adding keys to table sms_messages. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); $table->add_key('gateway', XMLDB_KEY_FOREIGN, ['gatewayid'], 'sms_gateways', ['id']); // Conditionally launch create table for sms_messages. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Define table sms_gateways to be created. $table = new xmldb_table('sms_gateways'); // Adding fields to table sms_gateways. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_field('gateway', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_field('enabled', XMLDB_TYPE_INTEGER, '2', null, XMLDB_NOTNULL, null, '1'); $table->add_field('config', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); // Adding keys to table sms_gateways. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for sms_gateways. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Main savepoint reached. upgrade_main_savepoint(true, 2024092000.01); } if ($oldversion < 2024092600.00) { // If h5plib_v126 is no longer present, remove it. if (!file_exists($CFG->dirroot . '/h5p/h5plib/v126/version.php')) { // Clean config. uninstall_plugin('h5plib', 'v126'); } // If h5plib_v127 is present, set it as the default one. if (file_exists($CFG->dirroot . '/h5p/h5plib/v127/version.php')) { set_config('h5plibraryhandler', 'h5plib_v127'); } // Main savepoint reached. upgrade_main_savepoint(true, 2024092600.00); } if ($oldversion < 2024100100.02) { upgrade_store_relative_url_sitehomepage(); // Main savepoint reached. upgrade_main_savepoint(true, 2024100100.02); } // Automatically generated Moodle v4.5.0 release upgrade line. // Put any upgrade step following this. if ($oldversion < 2024110400.00) { // Define field model to be added to ai_action_register. $table = new xmldb_table('ai_action_register'); $field = new xmldb_field('model', XMLDB_TYPE_CHAR, '50', null, null, null, null, null); // Conditionally launch add field model. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2024110400.00); } if ($oldversion < 2024110800.00) { // Delete settings that were removed from code. $settings = ['backup_general_questionbank', 'backup_import_questionbank', 'backup_auto_questionbank']; array_walk($settings, static fn($setting) => unset_config($setting, 'backup')); // Main savepoint reached. upgrade_main_savepoint(true, 2024110800.00); } if ($oldversion < 2024110800.02) { // Changing type of field value on table user_preferences to text. $table = new xmldb_table('user_preferences'); $field = new xmldb_field('value', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null, 'name'); // Launch change of type for field value. $dbman->change_field_type($table, $field); // Main savepoint reached. upgrade_main_savepoint(true, 2024110800.02); } if ($oldversion < 2024111500.00) { // Changing precision of field fullname on table course to (1333). $table = new xmldb_table('course'); $field = new xmldb_field('fullname', XMLDB_TYPE_CHAR, '1333', null, XMLDB_NOTNULL, null, null, 'sortorder'); // Launch change of precision for field fullname. $dbman->change_field_precision($table, $field); // Main savepoint reached. upgrade_main_savepoint(true, 2024111500.00); } if ($oldversion < 2024111500.01) { // Changing precision of field fullname on table course_request to (1333). $table = new xmldb_table('course_request'); $field = new xmldb_field('fullname', XMLDB_TYPE_CHAR, '1333', null, XMLDB_NOTNULL, null, null, 'id'); // Launch change of precision for field fullname. $dbman->change_field_precision($table, $field); // Main savepoint reached. upgrade_main_savepoint(true, 2024111500.01); } // Now we want to change the precision of course_request.shortname. // To do this, we need to first drop the index, then re-create it. if ($oldversion < 2024111500.02) { // Define index shortname (not unique) to be dropped form course_request. $table = new xmldb_table('course_request'); $index = new xmldb_index('shortname', XMLDB_INDEX_NOTUNIQUE, ['shortname']); // Conditionally launch drop index shortname. if ($dbman->index_exists($table, $index)) { $dbman->drop_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2024111500.02); } if ($oldversion < 2024111500.03) { // Changing precision of field shortname on table course_request to (255). $table = new xmldb_table('course_request'); $field = new xmldb_field('shortname', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null, 'fullname'); // Launch change of precision for field shortname. $dbman->change_field_precision($table, $field); // Main savepoint reached. upgrade_main_savepoint(true, 2024111500.03); } if ($oldversion < 2024111500.04) { // Define index shortname (not unique) to be added to course_request. $table = new xmldb_table('course_request'); $index = new xmldb_index('shortname', XMLDB_INDEX_NOTUNIQUE, ['shortname']); // Conditionally launch add index shortname. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2024111500.04); } if ($oldversion < 2024112900.01) { // Define table reportbuilder_user_filter to be created. $table = new xmldb_table('reportbuilder_user_filter'); // Adding fields to table reportbuilder_user_filter. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('reportid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('filterdata', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); $table->add_field('usercreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('usermodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); // Adding keys to table reportbuilder_user_filter. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); $table->add_key('reportid', XMLDB_KEY_FOREIGN, ['reportid'], 'reportbuilder_report', ['id']); $table->add_key('usercreated', XMLDB_KEY_FOREIGN, ['usercreated'], 'user', ['id']); $table->add_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Adding indexes to table reportbuilder_user_filter. $table->add_index('report-user', XMLDB_INDEX_UNIQUE, ['reportid', 'usercreated']); // Conditionally launch create table for reportbuilder_user_filter. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Main savepoint reached. upgrade_main_savepoint(true, 2024112900.01); } if ($oldversion < 2024112900.02) { // Structure to collect current user filter preferences. $userfilterdata = []; $select = $DB->sql_like('name', '?'); $params = [$DB->sql_like_escape('reportbuilder-report-') . '%']; $preferences = $DB->get_records_select('user_preferences', $select, $params, 'userid, name'); foreach ($preferences as $preference) { preg_match('/^reportbuilder-report-(?\d+)-/', $preference->name, $matches); $userfilterdata[$preference->userid][$matches['reportid']][] = $preference->value; } // Migrate user filter preferences to new schema (combining previously chunked values due to size limitation). foreach ($userfilterdata as $userid => $reportfilterdata) { foreach ($reportfilterdata as $reportid => $filterdata) { $DB->insert_record('reportbuilder_user_filter', (object) [ 'reportid' => $reportid, 'filterdata' => implode('', $filterdata), 'usercreated' => $userid, 'usermodified' => $userid, 'timecreated' => time(), 'timemodified' => time(), ]); } } // Clean up old user filter preferences. $DB->delete_records_select('user_preferences', $select, $params); // Main savepoint reached. upgrade_main_savepoint(true, 2024112900.02); } // Moodle 5.0 Upgrade. if ($oldversion < 2024121800.00) { $smsgateways = $DB->get_records('sms_gateways'); foreach ($smsgateways as $gateway) { $newconfig = json_decode($gateway->config); // Continue only if either the `returnurl` OR the `saveandreturn` property exists. if (property_exists($newconfig, "returnurl") || property_exists($newconfig, "saveandreturn")) { // Remove unnecessary data in the config. unset($newconfig->returnurl, $newconfig->saveandreturn); // Update the record with the new config. $gateway->config = json_encode($newconfig); $DB->update_record('sms_gateways', $gateway); } } // Main savepoint reached. upgrade_main_savepoint(true, 2024121800.00); } if ($oldversion < 2024121900.01) { // Enable mod_subsection unless 'keepsubsectiondisabled' is set. if ((empty($CFG->keepsubsectiondisabled) || !$CFG->keepsubsectiondisabled) && $DB->get_record('modules', ['name' => 'subsection'])) { $manager = \core_plugin_manager::resolve_plugininfo_class('mod'); $manager::enable_plugin('subsection', 1); } // Main savepoint reached. upgrade_main_savepoint(true, 2024121900.01); } if ($oldversion < 2025011700.02) { // Define table ai_providers to be created. $table = new xmldb_table('ai_providers'); // Adding fields to table ai_providers. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_field('provider', XMLDB_TYPE_CHAR, '255', null, XMLDB_NOTNULL, null, null); $table->add_field('enabled', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1'); $table->add_field('config', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, null); $table->add_field('actionconfig', XMLDB_TYPE_TEXT, null, null, null, null, null); // Adding keys to table ai_provider. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Adding indexes to table ai_provider. $table->add_index('provider', XMLDB_INDEX_NOTUNIQUE, ['provider']); // Conditionally launch create table for ai_provider. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Now the instance table exists, migrate the existing providers. upgrade_convert_ai_providers_to_instances(); // Main savepoint reached. upgrade_main_savepoint(true, 2025011700.02); } if ($oldversion < 2025012400.01) { // Remove the default value for the apiversion field. $table = new xmldb_table('badge_external_backpack'); $apiversionfield = new xmldb_field('apiversion', XMLDB_TYPE_CHAR, '12', null, XMLDB_NOTNULL, null, null); $dbman->change_field_default($table, $apiversionfield); // Main savepoint reached. upgrade_main_savepoint(true, 2025012400.01); } if ($oldversion < 2025013100.01) { // Remove imageauthorname, imageauthoremail and imageauthorurl fields for badges. $table = new xmldb_table('badge'); $fields = [ 'imageauthorname', 'imageauthoremail', 'imageauthorurl', ]; foreach ($fields as $field) { $field = new xmldb_field($field); if ($dbman->field_exists($table, $field)) { $dbman->drop_field($table, $field); } } // Main savepoint reached. upgrade_main_savepoint(true, 2025013100.01); } if ($oldversion < 2025022100.01) { // Define table ai_action_explain_text to be created. $table = new xmldb_table('ai_action_explain_text'); // Adding fields to table ai_action_explain_text. $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('prompt', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('responseid', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('fingerprint', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('generatedcontent', XMLDB_TYPE_TEXT, null, null, null, null, null); $table->add_field('finishreason', XMLDB_TYPE_CHAR, '128', null, null, null, null); $table->add_field('prompttokens', XMLDB_TYPE_INTEGER, '10', null, null, null, null); $table->add_field('completiontoken', XMLDB_TYPE_INTEGER, '10', null, null, null, null); // Adding keys to table ai_action_explain_text. $table->add_key('primary', XMLDB_KEY_PRIMARY, ['id']); // Conditionally launch create table for ai_action_explain_text. if (!$dbman->table_exists($table)) { $dbman->create_table($table); } // Add explain action config to the AI providers. upgrade_add_explain_action_to_ai_providers(); // Main savepoint reached. upgrade_main_savepoint(true, 2025022100.01); } if ($oldversion < 2025022100.02) { // Due to a code restriction on the upgrade, invoking any core functions is not permitted. // Thus, to acquire the list of provider plugins, // we should extract them from the `config_plugins` database table. $condition = $DB->sql_like('plugin', ':pattern'); $params = ['pattern' => 'aiprovider_%', 'name' => 'version']; $sql = "SELECT plugin FROM {config_plugins} WHERE {$condition} AND name = :name"; $providers = $DB->get_fieldset_sql($sql, $params); foreach ($providers as $provider) { // Replace the provider's language string with the provider component's name. if (get_string_manager()->string_exists('pluginname', $provider)) { $providername = get_string('pluginname', $provider); $sql = 'UPDATE {ai_action_register} SET provider = :provider WHERE LOWER(provider) = :providername'; $DB->execute($sql, ['provider' => $provider, 'providername' => strtolower($providername)]); } } // Main savepoint reached. upgrade_main_savepoint(true, 2025022100.02); } // Uninstall auth_cas and remove dependencies. if ($oldversion < 2025030500.00) { if (!file_exists($CFG->dirroot . "/auth/cas/version.php")) { uninstall_plugin('auth', 'cas'); // Remove the sensiblesettings config for auth_cas. $sensiblesettingsraw = explode(',', get_config('adminpresets', 'sensiblesettings')); $sensiblesettings = array_map('trim', $sensiblesettingsraw); if (($key = array_search('bind_pw@@auth_cas', $sensiblesettings)) !== false) { unset($sensiblesettings[$key]); } $sensiblesettings = implode(', ', $sensiblesettings); set_config('sensiblesettings', $sensiblesettings, 'adminpresets'); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030500.00); } if ($oldversion < 2025030500.01) { // If atto is no longer present, remove it. if (!file_exists("{$CFG->dirroot}/lib/editor/atto/version.php")) { // Remove each of the subplugins first. These are no longer on disk so the standard `uninstall_plugin` approach // on atto itself will not remove them. $plugins = array_keys(core_plugin_manager::instance()->get_plugins_of_type('atto')); // Now remove each. foreach ($plugins as $pluginname) { uninstall_plugin('atto', $pluginname); } // Finally uninstall the actual plugin. uninstall_plugin('editor', 'atto'); } upgrade_main_savepoint(true, 2025030500.01); } // Remove portfolio_mahara. if ($oldversion < 2025030600.02) { if (!file_exists($CFG->dirroot . "/portfolio/mahara/version.php")) { uninstall_plugin('portfolio', 'mahara'); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.02); } // Remove enrol_mnet. if ($oldversion < 2025030600.03) { if (!file_exists($CFG->dirroot . "/enrol/mnet/version.php")) { uninstall_plugin('enrol', 'mnet'); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.03); } // Remove block_mnet_hosts. if ($oldversion < 2025030600.04) { if (!file_exists($CFG->dirroot . "/blocks/mnet_hosts/version.php")) { uninstall_plugin('block', 'mnet_hosts'); // Delete all the admin preset plugin states concerning mnet_hosts in adminpresets_plug table. $DB->delete_records('adminpresets_plug', ['name' => 'mnet_hosts']); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.04); } // Remove auth_mnet. if ($oldversion < 2025030600.05) { if (!file_exists($CFG->dirroot . "/auth/mnet/version.php")) { uninstall_plugin('auth', 'mnet'); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.05); } if ($oldversion < 2025030600.06) { // If mlbackend_php is no longer present, remove it. if (!file_exists($CFG->dirroot . '/lib/mlbackend/php/version.php')) { // Clean config. uninstall_plugin('mlbackend', 'php'); // Change the processor if mlbackend_php is set. if (get_config('analytics', 'predictionsprocessor') === '\mlbackend_php\processor') { set_config('predictionsprocessor', '\mlbackend_python\processor', 'analytics'); // We can't be sure mlbackend_python is set up correctly, so we disable analytics. set_config('enableanalytics', false); } // Cleanup any references to mlbackend_php. $select = $DB->sql_like('predictionsprocessor', ':predictionsprocessor', false); $params = ['predictionsprocessor' => '%' . $DB->sql_like_escape('mlbackend_php') . '%']; $DB->set_field_select( table: 'analytics_models', newfield: 'predictionsprocessor', newvalue: null, select: $select, params: $params, ); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.06); } if ($oldversion < 2025030600.07) { $providers = $DB->get_records('ai_providers', ['enabled' => 1]); // Formatting the value. $value = ','. implode(',', array_column($providers, 'id')); // Create the order config setting. set_config('provider_order', $value, 'core_ai'); // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.07); } // Remove mnetservice_enrol. if ($oldversion < 2025030600.08) { if (!file_exists($CFG->dirroot . "/mnet/service/enrol/version.php")) { uninstall_plugin('mnetservice', 'enrol'); } // Main savepoint reached. upgrade_main_savepoint(true, 2025030600.08); } if ($oldversion < 2025031800.00) { // Add index for querying delegated sections. $table = new xmldb_table('course_sections'); $index = new xmldb_index('component_itemid', XMLDB_INDEX_NOTUNIQUE, ['component', 'itemid']); if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2025031800.00); } if ($oldversion < 2025031800.03) { // Define field penalty to be added to grade_grades. $table = new xmldb_table('grade_grades'); $field = new xmldb_field('deductedmark', XMLDB_TYPE_NUMBER, '10, 5', null, XMLDB_NOTNULL, null, '0', 'aggregationweight'); // Conditionally launch add field penalty. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2025031800.03); } if ($oldversion < 2025031800.04) { // Define field overriddenmark to be added to grade_grades. $table = new xmldb_table('grade_grades'); $field = new xmldb_field('overriddenmark', XMLDB_TYPE_NUMBER, '10, 5', null, XMLDB_NOTNULL, null, '0', 'deductedmark'); // Conditionally launch add field penalty. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2025031800.04); } if ($oldversion < 2025032800.01) { // Upgrade webp mime type for existing webp files. upgrade_create_async_mimetype_upgrade_task('image/webp', ['webp']); // Main savepoint reached. upgrade_main_savepoint(true, 2025032800.01); } // Remove chat and survey and respective analytics indicators. if ($oldversion < 2025040100.01) { $indicatorstoremove = []; $sqllikes = []; $sqlparams = []; if (!file_exists($CFG->dirroot . "/mod/survey/version.php")) { uninstall_plugin('mod', 'survey'); $DB->delete_records('adminpresets_plug', ['plugin' => 'mod', 'name' => 'survey']); $indicatorstoremove['survey'] = [ '\mod_survey\analytics\indicator\cognitive_depth', '\mod_survey\analytics\indicator\social_breadth', ]; $sqlparams['surveypluginname'] = '%' . $DB->sql_like_escape('mod_survey') . '%'; $sqllikes['survey'] = $DB->sql_like('indicators', ':surveypluginname'); } if (!file_exists($CFG->dirroot . "/mod/chat/version.php")) { uninstall_plugin('mod', 'chat'); $DB->delete_records('adminpresets_plug', ['plugin' => 'mod', 'name' => 'chat']); $indicatorstoremove['chat'] = [ '\mod_chat\analytics\indicator\cognitive_depth', '\mod_chat\analytics\indicator\social_breadth', ]; $sqlparams['chatpluginname'] = '%' . $DB->sql_like_escape('mod_chat') . '%'; $sqllikes['chat'] = $DB->sql_like('indicators', ':chatpluginname'); } foreach ($indicatorstoremove as $module => $indicators) { $models = $DB->get_recordset_select('analytics_models', $sqllikes[$module], $sqlparams); foreach ($models as $model) { $currentindicators = json_decode($model->indicators, true); if (!empty($indicators) && !empty($currentindicators)) { $newindicators = array_values(array_diff($currentindicators, $indicators)); $model->indicators = json_encode($newindicators); $DB->update_record('analytics_models', $model); } } $models->close(); } // Main savepoint reached. upgrade_main_savepoint(true, 2025040100.01); } // Remove overriddenmark field from grade_grades. if ($oldversion < 2025040700.00) { $table = new xmldb_table('grade_grades'); $field = new xmldb_field('overriddenmark'); if ($dbman->field_exists($table, $field)) { $dbman->drop_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2025040700.00); } // Automatically generated Moodle v5.0.0 release upgrade line. // Put any upgrade step following this. if ($oldversion < 2025041400.09) { // Define field systememail to be added to oauth2_issuer. $table = new xmldb_table('oauth2_issuer'); $field = new xmldb_field('systememail', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'loginpagename'); // Conditionally launch add field systememail. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2025041400.09); } if ($oldversion < 2025041401.08) { // Get all OpenAI providers. $records = $DB->get_records('ai_providers', ['provider' => 'aiprovider_openai\provider']); foreach ($records as $record) { $actionconfig = json_decode($record->actionconfig, true, 512); $originalactionconfig = $actionconfig; foreach ($actionconfig as $actionkey => $action) { $model = $action['settings']['model']; if ($model === 'gpt-4o' || $model === 'o1') { // Rename setting max_tokens to max_completion_tokens. if (isset($action['settings']['max_tokens'])) { $actionconfig[$actionkey]['settings']['max_completion_tokens'] = intval($action['settings']['max_tokens']); unset($actionconfig[$actionkey]['settings']['max_tokens']); } } // Cast settings for 'gpt-4o' model. if ($model === 'gpt-4o') { if (isset($action['settings']['top_p'])) { $actionconfig[$actionkey]['settings']['top_p'] = floatval($action['settings']['top_p']); } if (isset($action['settings']['presence_penalty'])) { $actionconfig[$actionkey]['settings']['presence_penalty'] = floatval($action['settings']['presence_penalty']); } if (isset($action['settings']['frequency_penalty'])) { $actionconfig[$actionkey]['settings']['frequency_penalty'] = floatval($action['settings']['frequency_penalty']); } } // Remove settings from 'o1' model. if ($model === 'o1') { if (isset($action['settings']['top_p'])) { unset($actionconfig[$actionkey]['settings']['top_p']); } if (isset($action['settings']['presence_penalty'])) { unset($actionconfig[$actionkey]['settings']['presence_penalty']); } if (isset($action['settings']['frequency_penalty'])) { unset($actionconfig[$actionkey]['settings']['frequency_penalty']); } } } if ($originalactionconfig !== $actionconfig) { $updatedrecord = new stdClass(); $updatedrecord->id = $record->id; $updatedrecord->actionconfig = json_encode($actionconfig); $DB->update_record('ai_providers', $updatedrecord); } } // Main savepoint reached. upgrade_main_savepoint(true, 2025041401.08); } if ($oldversion < 2025041401.10) { // A [name => url] map of new OIDC endpoints to be updated/created. $endpointuris = [ 'discovery_endpoint' => 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration', 'token_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/token', 'userinfo_endpoint' => 'https://graph.microsoft.com/oidc/userinfo', 'authorization_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', 'device_authorization_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/devicecode', 'end_session_endpoint' => 'https://login.microsoftonline.com/common/oauth2/v2.0/logout', 'kerberos_endpoint' => 'https://login.microsoftonline.com/common/kerberos', ]; // A [name] map of endpoints to be deleted. $deletedendpointuris = [ 'userpicture_endpoint', ]; // A [internalfield => externalfield] map of new OIDC-based user field mappings to be updated/created. $userfieldmappings = [ 'idnumber' => 'sub', 'firstname' => 'givenname', 'lastname' => 'familyname', 'email' => 'email', 'lang' => 'locale', ]; $admin = get_admin(); $adminid = $admin ? $admin->id : '0'; $microsoftservices = $DB->get_records('oauth2_issuer', ['servicetype' => 'microsoft']); foreach ($microsoftservices as $microsoftservice) { $time = time(); if (strpos($microsoftservice->baseurl, 'common') !== false) { // Multi-tenant endpoint, proceed with upgrade. // Insert/update the new endpoints. foreach ($endpointuris as $endpointname => $endpointuri) { $endpoint = ['issuerid' => $microsoftservice->id, 'name' => $endpointname]; $endpointid = $DB->get_field('oauth2_endpoint', 'id', $endpoint); if ($endpointid) { $endpoint = array_merge($endpoint, [ 'id' => $endpointid, 'url' => $endpointuri, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->update_record('oauth2_endpoint', $endpoint); } else { $endpoint = array_merge($endpoint, [ 'url' => $endpointuri, 'timecreated' => $time, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->insert_record('oauth2_endpoint', $endpoint); } } // Delete the old endpoints. foreach ($deletedendpointuris as $endpointname) { $endpoint = ['issuerid' => $microsoftservice->id, 'name' => $endpointname]; $DB->delete_records('oauth2_endpoint', $endpoint); } // Insert/update new user field mappings. foreach ($userfieldmappings as $internalfieldname => $externalfieldname) { $fieldmap = ['issuerid' => $microsoftservice->id, 'internalfield' => $internalfieldname]; $fieldmapid = $DB->get_field('oauth2_user_field_mapping', 'id', $fieldmap); if ($fieldmapid) { $fieldmap = array_merge($fieldmap, [ 'id' => $fieldmapid, 'externalfield' => $externalfieldname, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->update_record('oauth2_user_field_mapping', $fieldmap); } else { $fieldmap = array_merge($fieldmap, [ 'externalfield' => $externalfieldname, 'timecreated' => $time, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->insert_record('oauth2_user_field_mapping', $fieldmap); } } // Update the baseurl for the issuer. $microsoftservice->baseurl = 'https://login.microsoftonline.com/common/v2.0'; $microsoftservice->timemodified = $time; $microsoftservice->usermodified = $adminid; $DB->update_record('oauth2_issuer', $microsoftservice); } else { // Single-tenant endpoint, add discovery_endpoint if it doesn't exist. $url = $microsoftservice->baseurl; $url .= (substr($url, -1) === '/') ? '' : '/'; $url .= '.well-known/openid-configuration'; $endpoint = ['issuerid' => $microsoftservice->id, 'name' => 'discovery_endpoint']; $endpointid = $DB->get_field('oauth2_endpoint', 'id', $endpoint); if (!$endpointid) { $endpoint = array_merge($endpoint, [ 'url' => $url, 'timecreated' => $time, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->insert_record('oauth2_endpoint', $endpoint); } } } // Main savepoint reached. upgrade_main_savepoint(true, 2025041401.10); } if ($oldversion < 2025041403.05) { // Remove any orphaned competency evidence records (pointing to non-existing contexts). $DB->delete_records_select('competency_evidence', 'NOT EXISTS ( SELECT ctx.id FROM {context} ctx WHERE ctx.id = {competency_evidence}.contextid )'); // Main savepoint reached. upgrade_main_savepoint(true, 2025041403.05); } if ($oldversion < 2025041403.11) { // Define index hashcode (not unique) to be added to question_response_analysis. $table = new xmldb_table('question_response_analysis'); $index = new xmldb_index('hashcode', XMLDB_INDEX_NOTUNIQUE, ['hashcode']); // Conditionally launch add index hashcode. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Define index hashcode (not unique) to be added to question_statistics. $table = new xmldb_table('question_statistics'); $index = new xmldb_index('hashcode', XMLDB_INDEX_NOTUNIQUE, ['hashcode']); // Conditionally launch add index hashcode. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2025041403.11); } if ($oldversion < 2025041404.02) { // Fix Microsoft OAuth2 user field mappings to use OpenID Connect standard field names. // This corrects the mappings introduced in MDL-84432 which used non-standard field names // that only work with personal Microsoft accounts but not work/school (Entra ID) accounts. $userfieldmappings = [ 'firstname' => 'given_name', 'lastname' => 'family_name', ]; $admin = get_admin(); $adminid = $admin ? $admin->id : '0'; $microsoftservices = $DB->get_records('oauth2_issuer', ['servicetype' => 'microsoft']); foreach ($microsoftservices as $microsoftservice) { $time = time(); // Update user field mappings to use OpenID Connect standard field names. foreach ($userfieldmappings as $internalfieldname => $externalfieldname) { $fieldmap = ['issuerid' => $microsoftservice->id, 'internalfield' => $internalfieldname]; $fieldmapid = $DB->get_field('oauth2_user_field_mapping', 'id', $fieldmap); if ($fieldmapid) { $fieldmap = array_merge($fieldmap, [ 'id' => $fieldmapid, 'externalfield' => $externalfieldname, 'timemodified' => $time, 'usermodified' => $adminid, ]); $DB->update_record('oauth2_user_field_mapping', $fieldmap); } } } // Main savepoint reached. upgrade_main_savepoint(true, 2025041404.02); } if ($oldversion < 2025041404.04) { // Define field nextversion to be added to question_bank_entries. $table = new xmldb_table('question_bank_entries'); $field = new xmldb_field('nextversion', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'ownerid'); // Conditionally launch add field nextversion. if (!$dbman->field_exists($table, $field)) { $dbman->add_field($table, $field); } // Main savepoint reached. upgrade_main_savepoint(true, 2025041404.04); } if ($oldversion < 2025041404.06) { // Changing the default of field showactivitydates on table course to 1. $table = new xmldb_table('course'); $field = new xmldb_field('showactivitydates', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'originalcourseid'); // Launch change of default for field showactivitydates. $dbman->change_field_default($table, $field); // Main savepoint reached. upgrade_main_savepoint(true, 2025041404.06); } if ($oldversion < 2025041404.08) { // Define index nextruntime_classname (not unique) to be added to task_adhoc. $table = new xmldb_table('task_adhoc'); $index = new xmldb_index('nextruntime_classname', XMLDB_INDEX_NOTUNIQUE, ['nextruntime', 'classname']); // Conditionally launch add index nextruntime_classname. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2025041404.08); } if ($oldversion < 2025041404.09) { // Define index lastruntime_nextruntime (not unique) to be added to task_scheduled. $table = new xmldb_table('task_scheduled'); $index = new xmldb_index('lastruntime_nextruntime', XMLDB_INDEX_NOTUNIQUE, ['lastruntime', 'nextruntime']); // Conditionally launch add index lastruntime_nextruntime. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Main savepoint reached. upgrade_main_savepoint(true, 2025041404.09); } if ($oldversion < 2025041404.10) { \core_question\category_manager::fix_restored_category_parents(); // Main savepoint reached. upgrade_main_savepoint(true, 2025041404.10); } return true; } db/install.php000064400000001657152265520240007324 0ustar00. /** * auth_db installer script. * * @package auth_db * @copyright 2009 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ function xmldb_auth_db_install() { global $CFG, $DB; } db/tasks.php000064400000002304152265520240006771 0ustar00. /** * Task definition for auth_db. * @author Guy Thomas * @copyright Copyright (c) 2017 Blackboard Inc. * @package auth_db * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $tasks = array( array( 'classname' => '\auth_db\task\sync_users', 'blocking' => 0, 'minute' => 'R', 'hour' => 'R', 'day' => '*', 'month' => '*', 'dayofweek' => '*', 'disabled' => 1 ) ); db/upgrade.php000064400000003126152265520240007276 0ustar00. /** * DB authentication plugin upgrade code * * @package auth_db * @copyright 2017 Stephen Bourget * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Function to upgrade auth_db. * @param int $oldversion the version we are upgrading from * @return bool result */ function xmldb_auth_db_upgrade($oldversion) { // Automatically generated Moodle v4.2.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.3.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.4.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.5.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v5.0.0 release upgrade line. // Put any upgrade step following this. return true; } lang/en/auth_db.php000064400000013535152265520240010220 0ustar00. /** * Strings for component 'auth_db', language 'en'. * * @package auth_db * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['auth_dbcantconnect'] = 'Could not connect to the specified authentication database...'; $string['auth_dbdebugauthdb'] = 'Debug ADOdb'; $string['auth_dbdebugauthdbhelp'] = 'Debug ADOdb connection to external database - use when getting empty page during login. Not suitable for production sites.'; $string['auth_dbdeleteuser'] = 'Deleted user {$a->name} id {$a->id}'; $string['auth_dbdeleteusererror'] = 'Error deleting user {$a}'; $string['auth_dbdescription'] = 'This method uses an external database table to check whether a given username and password is valid. If the account is a new one, then information from other fields may also be copied across into Moodle.'; $string['auth_dbextencoding'] = 'External db encoding'; $string['auth_dbextencodinghelp'] = 'Encoding used in external database'; $string['auth_dbextrafields'] = 'These fields are optional. You can choose to pre-fill some Moodle user fields with information from the external database fields that you specify here.

If you leave these blank, then defaults will be used.

In either case, the user will be able to edit all of these fields after they log in.

'; $string['auth_dbfieldpass'] = 'Name of the field containing passwords'; $string['auth_dbfieldpass_key'] = 'Password field'; $string['auth_dbfielduser'] = 'Name of the field containing usernames. This field must be a varchar data type.'; $string['auth_dbfielduser_key'] = 'Username field'; $string['auth_dbhost'] = 'The computer hosting the database server. Use a system DSN entry if using ODBC. Use a PDO DSN entry if using PDO.'; $string['auth_dbhost_key'] = 'Host'; $string['auth_dbchangepasswordurl_key'] = 'Password-change URL'; $string['auth_dbinsertuser'] = 'Inserted user {$a->name} id {$a->id}'; $string['auth_dbinsertuserduplicate'] = 'Error inserting user {$a->username} - user with this username was already created through \'{$a->auth}\' plugin.'; $string['auth_dbinsertusererror'] = 'Error inserting user {$a}'; $string['auth_dbname'] = 'Name of the database itself. Leave empty if using an ODBC DSN. Leave empty if your PDO DSN already contains the database name.'; $string['auth_dbname_key'] = 'DB name'; $string['auth_dbpass'] = 'Password matching the above username'; $string['auth_dbpass_key'] = 'Password'; $string['auth_dbpasstype'] = '

Specify the format that the password field is using.

Use \'internal\' if you want the external database to manage usernames and email addresses, but Moodle to manage passwords. If you use \'internal\', you must provide a populated email address field in the external database, and you must enable the \auth_db\task\sync_users scheduled task. Moodle will send an email to new users with a temporary password.

'; $string['auth_dbpasstype_key'] = 'Password format'; $string['auth_dbreviveduser'] = 'Revived user {$a->name} id {$a->id}'; $string['auth_dbrevivedusererror'] = 'Error reviving user {$a}'; $string['auth_dbsaltedcrypt'] = 'Crypt one-way string hashing'; $string['auth_dbsetupsql'] = 'SQL setup command'; $string['auth_dbsetupsqlhelp'] = 'SQL command for special database setup, often used to setup communication encoding - example for MySQL and PostgreSQL: SET NAMES \'utf8\''; $string['auth_dbsuspenduser'] = 'Suspended user {$a->name} id {$a->id}'; $string['auth_dbsuspendusererror'] = 'Error suspending user {$a}'; $string['auth_dbsybasequoting'] = 'Use sybase quotes'; $string['auth_dbsybasequotinghelp'] = 'Sybase style single quote escaping - needed for MS SQL and some other databases. Do not use for MySQL!'; $string['auth_dbsyncuserstask'] = 'Synchronise users task'; $string['auth_dbtable'] = 'Name of the table in the database'; $string['auth_dbtable_key'] = 'Table'; $string['auth_dbtype'] = 'The database type (see the documentation ADOdb - Database Abstraction Layer for PHP for details).'; $string['auth_dbtype_key'] = 'Database'; $string['auth_dbupdateusers'] = 'Update users'; $string['auth_dbupdateusers_description'] = 'As well as inserting new users, update existing users.'; $string['auth_dbupdatinguser'] = 'Updating user {$a->name} id {$a->id}'; $string['auth_dbuser'] = 'Username with read access to the database'; $string['auth_dbuser_key'] = 'DB user'; $string['auth_dbuserstoadd'] = 'User entries to add: {$a}'; $string['auth_dbuserstoremove'] = 'User entries to remove: {$a}'; $string['auth_dbnoexttable'] = 'External table not specified.'; $string['auth_dbnouserfield'] = 'External user field not specified.'; $string['auth_dbcannotconnect'] = 'Cannot connect to external database.'; $string['auth_dbcannotreadtable'] = 'Cannot read external table.'; $string['auth_dbtableempty'] = 'External table is empty.'; $string['auth_dbcolumnlist'] = 'External table contains the following columns:
{$a}'; $string['auth_dbupdateerror'] = 'Error updating external database.'; $string['pluginname'] = 'External database'; $string['privacy:metadata'] = 'The External database authentication plugin does not store any personal data.'; version.php000064400000002227152265520240006750 0ustar00. /** * Version details * * @package auth_db * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2025041400; // The current plugin version (Date: YYYYMMDDXX). $plugin->requires = 2025040800; // Requires this Moodle version. $plugin->component = 'auth_db'; // Full name of the plugin (used for diagnostics) classes/privacy/provider.php000064400000002734152265520240012232 0ustar00. /** * Privacy Subsystem implementation for auth_db. * * @package auth_db * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace auth_db\privacy; defined('MOODLE_INTERNAL') || die(); /** * Privacy Subsystem for auth_db implementing null_provider. * * @copyright 2018 Carlos Escobedo * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\null_provider { /** * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * * @return string */ public static function get_reason(): string { return 'privacy:metadata'; } }classes/admin_setting_special_auth_configtext.php000064400000003163152265520240016520 0ustar00. /** * Special settings for auth_db password_link. * * @package auth_db * @copyright 2017 Stephen Bourget * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Special settings for auth_db password_link. * * @package auth_db * @copyright 2017 Stephen Bourget * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class auth_db_admin_setting_special_auth_configtext extends admin_setting_configtext { /** * We need to overwrite the global "alternate login url" setting if wayf is enabled. * * @param string $data Form data. * @return string Empty when no errors. */ public function write_setting($data) { if (get_config('auth_db', 'passtype') === 'internal') { // We need to clear the auth_db change password link. $data = ''; } return parent::write_setting($data); } } classes/task/sync_users.php000064400000003140152265520240012052 0ustar00. namespace auth_db\task; /** * Sync users task class * @package auth_db * @author Guy Thomas * @copyright Copyright (c) 2017 Blackboard Inc. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class sync_users extends \core\task\scheduled_task { /** * Name for this task. * * @return string */ public function get_name() { return get_string('auth_dbsyncuserstask', 'auth_db'); } /** * Run task for synchronising users. */ public function execute() { if (!is_enabled_auth('db')) { mtrace('auth_db plugin is disabled, synchronisation stopped'); return; } $dbauth = get_auth_plugin('db'); $config = get_config('auth_db'); $trace = new \text_progress_trace(); $update = !empty($config->updateusers); $dbauth->sync_users($trace, $update); } } upgrade.txt000064400000001650152265520240006741 0ustar00=== 4.5 Onwards === This file has been replaced by UPGRADING.md. See MDL-81125 for further information. === This files describes API changes in /auth/db/*, information provided here is intended especially for developers. === 3.3 === * The config.html file was migrated to use the admin settings API. The identifier for configuration data stored in config_plugins table was converted from 'auth/db' to 'auth_db'. === 3.1 === * The auth_plugin_db::clean_data() has been deprecated and will be removed in a future version. Please update to use core_user::clean_data() instead. === 2.9 === Some alterations have been made to the handling of case sensitity handling of passwords and password hashes which previously varied depending on database configuration: * Plain text password matching is now always case sensitive * sha1/md5 hash comparisons are now enforced case insensitive (as underlying they are hexidecimal values) cli/sync_users.php000064400000007013152265520240010225 0ustar00. /** * Extdb user sync script. * * This script is meant to be called from a system cronjob to * sync moodle user accounts with external database. * It is required when using internal passwords (== passwords not defined in external database). * * Sample cron entry: * # 5 minutes past 4am * 5 4 * * * sudo -u www-data /usr/bin/php /var/www/moodle/auth/db/cli/sync_users.php * * Notes: * - it is required to use the web server account when executing PHP CLI scripts * - you need to change the "www-data" to match the apache user account * - use "su" if "sudo" not available * - If you have a large number of users, you may want to raise the memory limits * by passing -d memory_limit=256M * - For debugging & better logging, you are encouraged to use in the command line: * -d log_errors=1 -d error_reporting=E_ALL -d display_errors=0 -d html_errors=0 * * Performance notes: * + The code is simpler, but not as optimized as its LDAP counterpart. * * @package auth_db * @copyright 2006 Martin Langhoff * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define('CLI_SCRIPT', true); require(__DIR__.'/../../../config.php'); require_once("$CFG->libdir/clilib.php"); // Now get cli options. list($options, $unrecognized) = cli_get_params(array('noupdate'=>false, 'verbose'=>false, 'help'=>false), array('n'=>'noupdate', 'v'=>'verbose', 'h'=>'help')); if ($unrecognized) { $unrecognized = implode("\n ", $unrecognized); cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); } if ($options['help']) { $help = "Execute user account sync with external database. The auth_db plugin must be enabled and properly configured. Options: -n, --noupdate Skip update of existing users -v, --verbose Print verbose progress information -h, --help Print out this help Example: \$ sudo -u www-data /usr/bin/php auth/db/cli/sync_users.php Sample cron entry: # 5 minutes past 4am 5 4 * * * sudo -u www-data /usr/bin/php /var/www/moodle/auth/db/cli/sync_users.php "; echo $help; die; } if (!is_enabled_auth('db')) { cli_error('auth_db plugin is disabled, synchronisation stopped', 2); } cli_problem('[AUTH DB] The sync users cron has been deprecated. Please use the scheduled task instead.'); // Abort execution of the CLI script if the \auth_db\task\sync_users is enabled. $task = \core\task\manager::get_scheduled_task('auth_db\task\sync_users'); if (!$task->get_disabled()) { cli_error('[AUTH DB] The scheduled task sync_users is enabled, the cron execution has been aborted.'); } if (empty($options['verbose'])) { $trace = new null_progress_trace(); } else { $trace = new text_progress_trace(); } $update = empty($options['noupdate']); /** @var auth_plugin_db $dbauth */ $dbauth = get_auth_plugin('db'); $result = $dbauth->sync_users($trace, $update); exit($result); tests/db_test.php000064400000056231152265520240010055 0ustar00. namespace auth_db; /** * External database auth sync tests, this also tests adodb drivers * that are matching our four supported Moodle database drivers. * * @package auth_db * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class db_test extends \advanced_testcase { /** @var string Original error log */ protected $oldlog; /** @var int The amount of users to create for the large user set deletion test */ protected $largedeletionsetsize = 128; public static function tearDownAfterClass(): void { global $DB; // Apply sqlsrv native driver error and logging default // settings while finishing the AdoDB tests. if ($DB->get_dbfamily() === 'mssql') { sqlsrv_configure("WarningsReturnAsErrors", false); sqlsrv_configure("LogSubsystems", SQLSRV_LOG_SYSTEM_OFF); sqlsrv_configure("LogSeverity", SQLSRV_LOG_SEVERITY_ERROR); } parent::tearDownAfterClass(); } protected function init_auth_database() { global $DB, $CFG; require_once("$CFG->dirroot/auth/db/auth.php"); // Discard error logs from AdoDB. $this->oldlog = ini_get('error_log'); ini_set('error_log', "$CFG->dataroot/testlog.log"); $dbman = $DB->get_manager(); set_config('extencoding', 'utf-8', 'auth_db'); set_config('host', $CFG->dbhost, 'auth_db'); set_config('user', $CFG->dbuser, 'auth_db'); set_config('pass', $CFG->dbpass, 'auth_db'); set_config('name', $CFG->dbname, 'auth_db'); if (!empty($CFG->dboptions['dbport'])) { set_config('host', $CFG->dbhost.':'.$CFG->dboptions['dbport'], 'auth_db'); } switch ($DB->get_dbfamily()) { case 'mysql': set_config('type', 'mysqli', 'auth_db'); set_config('setupsql', "SET NAMES 'UTF-8'", 'auth_db'); set_config('sybasequoting', '0', 'auth_db'); if (!empty($CFG->dboptions['dbsocket'])) { $dbsocket = $CFG->dboptions['dbsocket']; if ((strpos($dbsocket, '/') === false and strpos($dbsocket, '\\') === false)) { $dbsocket = ini_get('mysqli.default_socket'); } set_config('type', 'mysqli://'.rawurlencode($CFG->dbuser).':'.rawurlencode($CFG->dbpass).'@'.rawurlencode($CFG->dbhost).'/'.rawurlencode($CFG->dbname).'?socket='.rawurlencode($dbsocket), 'auth_db'); } break; case 'postgres': set_config('type', 'postgres7', 'auth_db'); $setupsql = "SET NAMES 'UTF-8'"; if (!empty($CFG->dboptions['dbschema'])) { $setupsql .= "; SET search_path = '".$CFG->dboptions['dbschema']."'"; } set_config('setupsql', $setupsql, 'auth_db'); set_config('sybasequoting', '0', 'auth_db'); if (!empty($CFG->dboptions['dbsocket']) and ($CFG->dbhost === 'localhost' or $CFG->dbhost === '127.0.0.1')) { if (strpos($CFG->dboptions['dbsocket'], '/') !== false) { $socket = $CFG->dboptions['dbsocket']; if (!empty($CFG->dboptions['dbport'])) { $socket .= ':' . $CFG->dboptions['dbport']; } set_config('host', $socket, 'auth_db'); } else { set_config('host', '', 'auth_db'); } } break; case 'mssql': set_config('type', 'mssqlnative', 'auth_db'); set_config('sybasequoting', '1', 'auth_db'); // The native sqlsrv driver uses a comma as separator between host and port. $dbhost = $CFG->dbhost; if (!empty($dboptions['dbport'])) { $dbhost .= ',' . $dboptions['dbport']; } set_config('host', $dbhost, 'auth_db'); break; default: throw new exception('Unknown database family ' . $DB->get_dbfamily()); } $table = new \xmldb_table('auth_db_users'); $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null); $table->add_field('pass', XMLDB_TYPE_CHAR, '255', null, null, null); $table->add_field('email', XMLDB_TYPE_CHAR, '255', null, null, null); $table->add_field('firstname', XMLDB_TYPE_CHAR, '255', null, null, null); $table->add_field('lastname', XMLDB_TYPE_CHAR, '255', null, null, null); $table->add_field('animal', XMLDB_TYPE_CHAR, '255', null, null, null); $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); if ($dbman->table_exists($table)) { $dbman->drop_table($table); } $dbman->create_table($table); set_config('table', $CFG->prefix.'auth_db_users', 'auth_db'); set_config('fielduser', 'name', 'auth_db'); set_config('fieldpass', 'pass', 'auth_db'); set_config('field_map_lastname', 'lastname', 'auth_db'); set_config('field_updatelocal_lastname', 'oncreate', 'auth_db'); set_config('field_lock_lastname', 'unlocked', 'auth_db'); // Setu up field mappings. set_config('field_map_email', 'email', 'auth_db'); set_config('field_updatelocal_email', 'oncreate', 'auth_db'); set_config('field_updateremote_email', '0', 'auth_db'); set_config('field_lock_email', 'unlocked', 'auth_db'); // Create a user profile field and add mapping to it. $this->getDataGenerator()->create_custom_profile_field(['shortname' => 'pet', 'name' => 'Pet', 'datatype' => 'text']); set_config('field_map_profile_field_pet', 'animal', 'auth_db'); set_config('field_updatelocal_profile_field_pet', 'oncreate', 'auth_db'); set_config('field_updateremote_profile_field_pet', '0', 'auth_db'); set_config('field_lock_profile_field_pet', 'unlocked', 'auth_db'); // Init the rest of settings. set_config('passtype', 'plaintext', 'auth_db'); set_config('changepasswordurl', '', 'auth_db'); set_config('debugauthdb', 0, 'auth_db'); set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth_db'); } protected function cleanup_auth_database() { global $DB; $dbman = $DB->get_manager(); $table = new \xmldb_table('auth_db_users'); $dbman->drop_table($table); ini_set('error_log', $this->oldlog); } public function test_plugin(): void { global $DB, $CFG; require_once($CFG->dirroot . '/user/profile/lib.php'); $this->resetAfterTest(true); // NOTE: It is strongly discouraged to create new tables in advanced_testcase classes, // but there is no other simple way to test ext database enrol sync, so let's // disable transactions are try to cleanup after the tests. $this->preventResetByRollback(); $this->init_auth_database(); /** @var auth_plugin_db $auth */ $auth = get_auth_plugin('db'); $authdb = $auth->db_init(); // Test adodb may access the table. $user1 = (object)array('name'=>'u1', 'pass'=>'heslo', 'email'=>'u1@example.com'); $user1->id = $DB->insert_record('auth_db_users', $user1); $sql = "SELECT * FROM {$auth->config->table}"; $rs = $authdb->Execute($sql); $this->assertInstanceOf('ADORecordSet', $rs); $this->assertFalse($rs->EOF); $fields = $rs->FetchRow(); $this->assertTrue(is_array($fields)); $this->assertTrue($rs->EOF); $rs->Close(); $authdb->Close(); // Test bulk user account creation. $user2 = (object)['name' => 'u2', 'pass' => 'heslo', 'email' => 'u2@example.com', 'animal' => 'cat']; $user2->id = $DB->insert_record('auth_db_users', $user2); $user3 = (object)array('name'=>'admin', 'pass'=>'heslo', 'email'=>'admin@example.com'); // Should be skipped. $user3->id = $DB->insert_record('auth_db_users', $user3); $this->assertCount(2, $DB->get_records('user')); $trace = new \null_progress_trace(); // Sync users and make sure that two events user_created werer triggered. $sink = $this->redirectEvents(); $auth->sync_users($trace, false); $events = $sink->get_events(); $sink->close(); $this->assertCount(2, $events); $this->assertTrue($events[0] instanceof \core\event\user_created); $this->assertTrue($events[1] instanceof \core\event\user_created); // Assert the two users were created. $this->assertEquals(4, $DB->count_records('user')); $u1 = $DB->get_record('user', array('username'=>$user1->name, 'auth'=>'db')); $this->assertSame($user1->email, $u1->email); $this->assertEmpty(profile_user_record($u1->id)->pet); $u2 = $DB->get_record('user', array('username'=>$user2->name, 'auth'=>'db')); $this->assertSame($user2->email, $u2->email); $this->assertSame($user2->animal, profile_user_record($u2->id)->pet); $admin = $DB->get_record('user', array('username'=>'admin', 'auth'=>'manual')); $this->assertNotEmpty($admin); // Test sync updates. $user2b = clone($user2); $user2b->email = 'u2b@example.com'; $user2b->animal = 'dog'; $DB->update_record('auth_db_users', $user2b); $auth->sync_users($trace, false); $this->assertEquals(4, $DB->count_records('user')); $u2 = $DB->get_record('user', array('username'=>$user2->name)); $this->assertSame($user2->email, $u2->email); $this->assertSame($user2->animal, profile_user_record($u2->id)->pet); $auth->sync_users($trace, true); $this->assertEquals(4, $DB->count_records('user')); $u2 = $DB->get_record('user', array('username'=>$user2->name)); $this->assertSame($user2->email, $u2->email); set_config('field_updatelocal_email', 'onlogin', 'auth_db'); $auth->config->field_updatelocal_email = 'onlogin'; set_config('field_updatelocal_profile_field_pet', 'onlogin', 'auth_db'); $auth->config->field_updatelocal_profile_field_pet = 'onlogin'; $auth->sync_users($trace, false); $this->assertEquals(4, $DB->count_records('user')); $u2 = $DB->get_record('user', array('username'=>$user2->name)); $this->assertSame($user2->email, $u2->email); $auth->sync_users($trace, true); $this->assertEquals(4, $DB->count_records('user')); $u2 = $DB->get_record('user', array('username'=>$user2->name)); $this->assertSame($user2b->email, $u2->email); $this->assertSame($user2b->animal, profile_user_record($u2->id)->pet); // Test sync deletes and suspends. $DB->delete_records('auth_db_users', array('id'=>$user2->id)); $this->assertCount(2, $DB->get_records('auth_db_users')); unset($user2); unset($user2b); $auth->sync_users($trace, false); $this->assertEquals(4, $DB->count_records('user')); $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1))); $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1))); set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth_db'); $auth->config->removeuser = AUTH_REMOVEUSER_SUSPEND; $auth->sync_users($trace, false); $this->assertEquals(4, $DB->count_records('user')); $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1))); $this->assertEquals(1, $DB->count_records('user', array('suspended'=>1))); $user2 = (object)array('name'=>'u2', 'pass'=>'heslo', 'email'=>'u2@example.com'); $user2->id = $DB->insert_record('auth_db_users', $user2); $auth->sync_users($trace, false); $this->assertEquals(4, $DB->count_records('user')); $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1))); $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1))); $DB->delete_records('auth_db_users', array('id'=>$user2->id)); set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth_db'); $auth->config->removeuser = AUTH_REMOVEUSER_FULLDELETE; $auth->sync_users($trace, false); $this->assertEquals(4, $DB->count_records('user')); $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1))); $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1))); $user2 = (object)array('name'=>'u2', 'pass'=>'heslo', 'email'=>'u2@example.com'); $user2->id = $DB->insert_record('auth_db_users', $user2); $auth->sync_users($trace, false); $this->assertEquals(5, $DB->count_records('user')); $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1))); $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1))); // Test user_login(). $user3 = (object)array('name'=>'u3', 'pass'=>'heslo', 'email'=>'u3@example.com'); $user3->id = $DB->insert_record('auth_db_users', $user3); $this->assertFalse($auth->user_login('u4', 'heslo')); $this->assertTrue($auth->user_login('u1', 'heslo')); $this->assertFalse($DB->record_exists('user', array('username'=>'u3', 'auth'=>'db'))); $this->assertTrue($auth->user_login('u3', 'heslo')); $this->assertFalse($DB->record_exists('user', array('username'=>'u3', 'auth'=>'db'))); set_config('passtype', 'md5', 'auth_db'); $auth->config->passtype = 'md5'; $user3->pass = md5('heslo'); $DB->update_record('auth_db_users', $user3); $this->assertTrue($auth->user_login('u3', 'heslo')); // Test user created to see if the checking happens strictly. $usermd5 = (object)['name' => 'usermd5', 'pass' => '0e462097431906509019562988736854']; $usermd5->id = $DB->insert_record('auth_db_users', $usermd5); // md5('240610708') === '0e462097431906509019562988736854'. $this->assertTrue($auth->user_login('usermd5', '240610708')); $this->assertFalse($auth->user_login('usermd5', 'QNKCDZO')); set_config('passtype', 'sh1', 'auth_db'); $auth->config->passtype = 'sha1'; $user3->pass = sha1('heslo'); $DB->update_record('auth_db_users', $user3); $this->assertTrue($auth->user_login('u3', 'heslo')); // Test user created to see if the checking happens strictly. $usersha1 = (object)['name' => 'usersha1', 'pass' => '0e66507019969427134894567494305185566735']; $usersha1->id = $DB->insert_record('auth_db_users', $usersha1); // sha1('aaroZmOk') === '0e66507019969427134894567494305185566735'. $this->assertTrue($auth->user_login('usersha1', 'aaroZmOk')); $this->assertFalse($auth->user_login('usersha1', 'aaK1STfY')); set_config('passtype', 'saltedcrypt', 'auth_db'); $auth->config->passtype = 'saltedcrypt'; $user3->pass = password_hash('heslo', PASSWORD_BCRYPT); $DB->update_record('auth_db_users', $user3); $this->assertTrue($auth->user_login('u3', 'heslo')); set_config('passtype', 'internal', 'auth_db'); $auth->config->passtype = 'internal'; create_user_record('u3', 'heslo', 'db'); $this->assertTrue($auth->user_login('u3', 'heslo')); $DB->delete_records('auth_db_users', array('id'=>$user3->id)); set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth_db'); $auth->config->removeuser = AUTH_REMOVEUSER_KEEP; $this->assertTrue($auth->user_login('u3', 'heslo')); set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth_db'); $auth->config->removeuser = AUTH_REMOVEUSER_SUSPEND; $this->assertFalse($auth->user_login('u3', 'heslo')); set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth_db'); $auth->config->removeuser = AUTH_REMOVEUSER_FULLDELETE; $this->assertFalse($auth->user_login('u3', 'heslo')); set_config('passtype', 'sh1', 'auth_db'); $auth->config->passtype = 'sha1'; $this->assertFalse($auth->user_login('u3', 'heslo')); // Test login create and update. $user4 = (object)array('name'=>'u4', 'pass'=>'heslo', 'email'=>'u4@example.com'); $user4->id = $DB->insert_record('auth_db_users', $user4); set_config('passtype', 'plaintext', 'auth_db'); $auth->config->passtype = 'plaintext'; $iuser4 = create_user_record('u4', 'heslo', 'db'); $this->assertNotEmpty($iuser4); $this->assertSame($user4->name, $iuser4->username); $this->assertSame($user4->email, $iuser4->email); $this->assertSame('db', $iuser4->auth); $this->assertSame($CFG->mnet_localhost_id, $iuser4->mnethostid); $user4b = clone($user4); $user4b->email = 'u4b@example.com'; $DB->update_record('auth_db_users', $user4b); set_config('field_updatelocal_email', 'oncreate', 'auth_db'); $auth->config->field_updatelocal_email = 'oncreate'; update_user_record('u4'); $iuser4 = $DB->get_record('user', array('id'=>$iuser4->id)); $this->assertSame($user4->email, $iuser4->email); set_config('field_updatelocal_email', 'onlogin', 'auth_db'); $auth->config->field_updatelocal_email = 'onlogin'; update_user_record('u4'); $iuser4 = $DB->get_record('user', array('id'=>$iuser4->id)); $this->assertSame($user4b->email, $iuser4->email); // Test user_exists() $this->assertTrue($auth->user_exists('u1')); $this->assertTrue($auth->user_exists('admin')); $this->assertFalse($auth->user_exists('u3')); $this->assertTrue($auth->user_exists('u4')); $this->cleanup_auth_database(); } /** * Testing the function _colonscope() from ADOdb. */ public function test_adodb_colonscope(): void { global $CFG; require_once($CFG->libdir.'/adodb/adodb.inc.php'); require_once($CFG->libdir.'/adodb/drivers/adodb-odbc.inc.php'); require_once($CFG->libdir.'/adodb/drivers/adodb-db2ora.inc.php'); $this->resetAfterTest(false); $sql = "select * from table WHERE column=:1 AND anothercolumn > :0"; $arr = array('b', 1); list($sqlout, $arrout) = _colonscope($sql,$arr); $this->assertEquals("select * from table WHERE column=? AND anothercolumn > ?", $sqlout); $this->assertEquals(array(1, 'b'), $arrout); } /** * Testing the clean_data() method. */ public function test_clean_data(): void { global $DB; $this->resetAfterTest(false); $this->preventResetByRollback(); $this->init_auth_database(); $auth = get_auth_plugin('db'); $auth->db_init(); // Create users on external table. $extdbuser1 = (object)array('name'=>'u1', 'pass'=>'heslo', 'email'=>'u1@example.com'); $extdbuser1->id = $DB->insert_record('auth_db_users', $extdbuser1); // User with malicious data on the name (won't be imported). $extdbuser2 = (object)array('name'=>'userxss', 'pass'=>'heslo', 'email'=>'xssuser@example.com'); $extdbuser2->id = $DB->insert_record('auth_db_users', $extdbuser2); $extdbuser3 = (object)array('name'=>'u3', 'pass'=>'heslo', 'email'=>'u3@example.com', 'lastname' => 'userxss'); $extdbuser3->id = $DB->insert_record('auth_db_users', $extdbuser3); $trace = new \null_progress_trace(); // Let's test user sync make sure still works as expected.. $auth->sync_users($trace, true); $this->assertDebuggingCalled("The property 'lastname' has invalid data and has been cleaned."); // User with correct data, should be equal to external db. $user1 = $DB->get_record('user', array('email'=> $extdbuser1->email, 'auth'=>'db')); $this->assertEquals($extdbuser1->name, $user1->username); $this->assertEquals($extdbuser1->email, $user1->email); // Get the user on moodle user table. $user2 = $DB->get_record('user', array('email'=> $extdbuser2->email, 'auth'=>'db')); $user3 = $DB->get_record('user', array('email'=> $extdbuser3->email, 'auth'=>'db')); $this->assertEmpty($user2); $this->assertEquals($extdbuser3->name, $user3->username); $this->assertEquals('useralert(1);xss', $user3->lastname); $this->cleanup_auth_database(); } /** * Testing the deletion of a user when there are many users in the external DB. */ public function test_deleting_with_many_users(): void { global $DB; $this->resetAfterTest(true); $this->preventResetByRollback(); $this->init_auth_database(); $auth = get_auth_plugin('db'); $auth->db_init(); // Set to delete from moodle when missing from DB. set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth_db'); $auth->config->removeuser = AUTH_REMOVEUSER_FULLDELETE; // Create users. $users = []; for ($i = 0; $i < $this->largedeletionsetsize; $i++) { $user = (object)array('username' => "u$i", 'name' => "u$i", 'pass' => 'heslo', 'email' => "u$i@example.com"); $user->id = $DB->insert_record('auth_db_users', $user); $users[] = $user; } // Sync to moodle. $trace = new \null_progress_trace(); $auth->sync_users($trace, true); // Check user is there. $user = array_shift($users); $moodleuser = $DB->get_record('user', array('email' => $user->email, 'auth' => 'db')); $this->assertNotNull($moodleuser); $this->assertEquals($user->username, $moodleuser->username); // Delete a user. $DB->delete_records('auth_db_users', array('id' => $user->id)); // Sync again. $auth->sync_users($trace, true); // Check user is no longer there. $moodleuser = $DB->get_record('user', array('id' => $moodleuser->id)); $this->assertFalse($auth->user_login($user->username, 'heslo')); $this->assertEquals(1, $moodleuser->deleted); // Make sure it was the only user deleted. $numberdeleted = $DB->count_records('user', array('deleted' => 1, 'auth' => 'db')); $this->assertEquals(1, $numberdeleted); $this->cleanup_auth_database(); } } auth.php000064400000072240152265520240006226 0ustar00. /** * Authentication Plugin: External Database Authentication * * Checks against an external database. * * @package auth_db * @author Martin Dougiamas * @license http://www.gnu.org/copyleft/gpl.html GNU Public License */ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/authlib.php'); /** * External database authentication plugin. */ class auth_plugin_db extends auth_plugin_base { /** * Constructor. */ function __construct() { global $CFG; require_once($CFG->libdir.'/adodb/adodb.inc.php'); $this->authtype = 'db'; $this->config = get_config('auth_db'); $this->errorlogtag = '[AUTH DB] '; if (empty($this->config->extencoding)) { $this->config->extencoding = 'utf-8'; } } /** * Returns true if the username and password work and false if they are * wrong or don't exist. * * @param string $username The username * @param string $password The password * @return bool Authentication success or failure. */ function user_login($username, $password) { global $CFG, $DB; if ($this->is_configured() === false) { debugging(get_string('auth_notconfigured', 'auth', $this->authtype)); return false; } $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding); $extpassword = core_text::convert($password, 'utf-8', $this->config->extencoding); if ($this->is_internal()) { // Lookup username externally, but resolve // password locally -- to support backend that // don't track passwords. if (isset($this->config->removeuser) and $this->config->removeuser == AUTH_REMOVEUSER_KEEP) { // No need to connect to external database in this case because users are never removed and we verify password locally. if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) { return validate_internal_user_password($user, $password); } else { return false; } } $authdb = $this->db_init(); $rs = $authdb->Execute("SELECT * FROM {$this->config->table} WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'"); if (!$rs) { $authdb->Close(); debugging(get_string('auth_dbcantconnect','auth_db')); return false; } if (!$rs->EOF) { $rs->Close(); $authdb->Close(); // User exists externally - check username/password internally. if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) { return validate_internal_user_password($user, $password); } } else { $rs->Close(); $authdb->Close(); // User does not exist externally. return false; } } else { // Normal case: use external db for both usernames and passwords. $authdb = $this->db_init(); $rs = $authdb->Execute("SELECT {$this->config->fieldpass} FROM {$this->config->table} WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'"); if (!$rs) { $authdb->Close(); debugging(get_string('auth_dbcantconnect','auth_db')); return false; } if ($rs->EOF) { $authdb->Close(); return false; } $fields = array_change_key_case($rs->fields, CASE_LOWER); $fromdb = $fields[strtolower($this->config->fieldpass)]; $rs->Close(); $authdb->Close(); if ($this->config->passtype === 'plaintext') { return ($fromdb === $extpassword); } else if ($this->config->passtype === 'md5') { return (strtolower($fromdb) === md5($extpassword)); } else if ($this->config->passtype === 'sha1') { return (strtolower($fromdb) === sha1($extpassword)); } else if ($this->config->passtype === 'saltedcrypt') { return password_verify($extpassword, $fromdb); } else { return false; } } } /** * Connect to external database. * * @return ADOConnection * @throws moodle_exception */ function db_init() { if ($this->is_configured() === false) { throw new moodle_exception('auth_dbcantconnect', 'auth_db'); } // Connect to the external database (forcing new connection). $authdb = ADONewConnection($this->config->type); if (!empty($this->config->debugauthdb)) { $authdb->debug = true; ob_start(); //Start output buffer to allow later use of the page headers. } $authdb->Connect($this->config->host, $this->config->user, $this->config->pass, $this->config->name, true); $authdb->SetFetchMode(ADODB_FETCH_ASSOC); if (!empty($this->config->setupsql)) { $authdb->Execute($this->config->setupsql); } return $authdb; } /** * Returns user attribute mappings between moodle and the external database. * * @return array */ function db_attributes() { $moodleattributes = array(); // If we have custom fields then merge them with user fields. $customfields = $this->get_custom_user_profile_fields(); if (!empty($customfields) && !empty($this->userfields)) { $userfields = array_merge($this->userfields, $customfields); } else { $userfields = $this->userfields; } foreach ($userfields as $field) { if (!empty($this->config->{"field_map_$field"})) { $moodleattributes[$field] = $this->config->{"field_map_$field"}; } } $moodleattributes['username'] = $this->config->fielduser; return $moodleattributes; } /** * Reads any other information for a user from external database, * then returns it in an array. * * @param string $username * @return array */ function get_userinfo($username) { global $CFG; $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding); $authdb = $this->db_init(); // Array to map local fieldnames we want, to external fieldnames. $selectfields = $this->db_attributes(); $result = array(); // If at least one field is mapped from external db, get that mapped data. if ($selectfields) { $select = array(); $fieldcount = 0; foreach ($selectfields as $localname=>$externalname) { // Without aliasing, multiple occurrences of the same external // name can coalesce in only occurrence in the result. $select[] = "$externalname AS F".$fieldcount; $fieldcount++; } $select = implode(', ', $select); $sql = "SELECT $select FROM {$this->config->table} WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'"; if ($rs = $authdb->Execute($sql)) { if (!$rs->EOF) { $fields = $rs->FetchRow(); // Convert the associative array to an array of its values so we don't have to worry about the case of its keys. $fields = array_values($fields); foreach (array_keys($selectfields) as $index => $localname) { $value = $fields[$index]; $result[$localname] = core_text::convert($value, $this->config->extencoding, 'utf-8'); } } $rs->Close(); } } $authdb->Close(); return $result; } /** * Change a user's password. * * @param stdClass $user User table object * @param string $newpassword Plaintext password * @return bool True on success */ function user_update_password($user, $newpassword) { global $DB; if ($this->is_internal()) { $puser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST); // This will also update the stored hash to the latest algorithm // if the existing hash is using an out-of-date algorithm (or the // legacy md5 algorithm). if (update_internal_user_password($puser, $newpassword)) { $user->password = $puser->password; return true; } else { return false; } } else { // We should have never been called! return false; } } /** * Synchronizes user from external db to moodle user table. * * Sync should be done by using idnumber attribute, not username. * You need to pass firstsync parameter to function to fill in * idnumbers if they don't exists in moodle user table. * * Syncing users removes (disables) users that don't exists anymore in external db. * Creates new users and updates coursecreator status of users. * * This implementation is simpler but less scalable than the one found in the LDAP module. * * @param progress_trace $trace * @param bool $do_updates Optional: set to true to force an update of existing accounts * @return int 0 means success, 1 means failure */ function sync_users(progress_trace $trace, $do_updates=false) { global $CFG, $DB; require_once($CFG->dirroot . '/user/lib.php'); // List external users. $userlist = $this->get_userlist(); // Delete obsolete internal users. if (!empty($this->config->removeuser)) { $suspendselect = ""; if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { $suspendselect = "AND u.suspended = 0"; } // Find obsolete users. if (count($userlist)) { $removeusers = array(); $params['authtype'] = $this->authtype; $sql = "SELECT u.id, u.username FROM {user} u WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect"; $params['mnethostid'] = $CFG->mnet_localhost_id; $internalusersrs = $DB->get_recordset_sql($sql, $params); $usernamelist = array_flip($userlist); foreach ($internalusersrs as $internaluser) { if (!array_key_exists($internaluser->username, $usernamelist)) { $removeusers[] = $internaluser; } } $internalusersrs->close(); } else { $sql = "SELECT u.id, u.username FROM {user} u WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect"; $params = array(); $params['authtype'] = $this->authtype; $params['mnethostid'] = $CFG->mnet_localhost_id; $removeusers = $DB->get_records_sql($sql, $params); } if (!empty($removeusers)) { $trace->output(get_string('auth_dbuserstoremove', 'auth_db', count($removeusers))); foreach ($removeusers as $user) { if ($this->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) { delete_user($user); $trace->output(get_string('auth_dbdeleteuser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1); } else if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { $updateuser = new stdClass(); $updateuser->id = $user->id; $updateuser->suspended = 1; user_update_user($updateuser, false); $trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1); } } } unset($removeusers); } if (!count($userlist)) { // Exit right here, nothing else to do. $trace->finished(); return 0; } // Update existing accounts. if ($do_updates) { // Narrow down what fields we need to update. $all_keys = array_keys(get_object_vars($this->config)); $updatekeys = array(); foreach ($all_keys as $key) { if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) { if ($this->config->{$key} === 'onlogin') { array_push($updatekeys, $match[1]); // The actual key name. } } } unset($all_keys); unset($key); // Only go ahead if we actually have fields to update locally. if (!empty($updatekeys)) { $update_users = array(); // All the drivers can cope with chunks of 10,000. See line 4491 of lib/dml/tests/dml_est.php $userlistchunks = array_chunk($userlist , 10000); foreach($userlistchunks as $userlistchunk) { list($in_sql, $params) = $DB->get_in_or_equal($userlistchunk, SQL_PARAMS_NAMED, 'u', true); $params['authtype'] = $this->authtype; $params['mnethostid'] = $CFG->mnet_localhost_id; $sql = "SELECT u.id, u.username, u.suspended FROM {user} u WHERE u.auth = :authtype AND u.deleted = 0 AND u.mnethostid = :mnethostid AND u.username {$in_sql}"; $update_users = $update_users + $DB->get_records_sql($sql, $params); } if ($update_users) { $trace->output("User entries to update: ".count($update_users)); foreach ($update_users as $user) { if ($this->update_user_record($user->username, $updatekeys, false, (bool) $user->suspended)) { $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1); } else { $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id))." - ".get_string('skipped'), 1); } } unset($update_users); } } } // Create missing accounts. // NOTE: this is very memory intensive and generally inefficient. $suspendselect = ""; if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { $suspendselect = "AND u.suspended = 0"; } $sql = "SELECT u.id, u.username FROM {user} u WHERE u.auth=:authtype AND u.deleted='0' AND mnethostid=:mnethostid $suspendselect"; $users = $DB->get_records_sql($sql, array('authtype'=>$this->authtype, 'mnethostid'=>$CFG->mnet_localhost_id)); // Simplify down to usernames. $usernames = array(); if (!empty($users)) { foreach ($users as $user) { array_push($usernames, $user->username); } unset($users); } $add_users = array_diff($userlist, $usernames); unset($usernames); if (!empty($add_users)) { $trace->output(get_string('auth_dbuserstoadd','auth_db',count($add_users))); // Do not use transactions around this foreach, we want to skip problematic users, not revert everything. foreach($add_users as $user) { $username = $user; if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) { if ($olduser = $DB->get_record('user', array('username' => $username, 'deleted' => 0, 'suspended' => 1, 'mnethostid' => $CFG->mnet_localhost_id, 'auth' => $this->authtype))) { $updateuser = new stdClass(); $updateuser->id = $olduser->id; $updateuser->suspended = 0; user_update_user($updateuser); $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name' => $username, 'id' => $olduser->id)), 1); continue; } } // Do not try to undelete users here, instead select suspending if you ever expect users will reappear. // Prep a few params. $user = $this->get_userinfo_asobj($user); $user->username = $username; $user->confirmed = 1; $user->auth = $this->authtype; $user->mnethostid = $CFG->mnet_localhost_id; if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) { $trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)), 1); continue; } try { $id = user_create_user($user, false, false); // It is truly a new user. $trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)), 1); } catch (moodle_exception $e) { $trace->output(get_string('auth_dbinsertusererror', 'auth_db', $user->username), 1); continue; } // If relevant, tag for password generation. if ($this->is_internal()) { set_user_preference('auth_forcepasswordchange', 1, $id); set_user_preference('create_password', 1, $id); } // Save custom profile fields here. require_once($CFG->dirroot . '/user/profile/lib.php'); $user->id = $id; profile_save_data($user); // Make sure user context is present. context_user::instance($id); \core\event\user_created::create_from_userid($id)->trigger(); } unset($add_users); } $trace->finished(); return 0; } function user_exists($username) { // Init result value. $result = false; $extusername = core_text::convert($username, 'utf-8', $this->config->extencoding); $authdb = $this->db_init(); $rs = $authdb->Execute("SELECT * FROM {$this->config->table} WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' "); if (!$rs) { throw new \moodle_exception('auth_dbcantconnect', 'auth_db'); } else if (!$rs->EOF) { // User exists externally. $result = true; } $authdb->Close(); return $result; } function get_userlist() { // Init result value. $result = array(); $authdb = $this->db_init(); // Fetch userlist. $rs = $authdb->Execute("SELECT {$this->config->fielduser} FROM {$this->config->table} "); if (!$rs) { throw new \moodle_exception('auth_dbcantconnect', 'auth_db'); } else if (!$rs->EOF) { while ($rec = $rs->FetchRow()) { $rec = array_change_key_case((array)$rec, CASE_LOWER); array_push($result, $rec[strtolower($this->config->fielduser)]); } } $authdb->Close(); return $result; } /** * Reads user information from DB and return it in an object. * * @param string $username username * @return stdClass */ function get_userinfo_asobj($username) { $user_array = truncate_userinfo($this->get_userinfo($username)); $user = new stdClass(); foreach($user_array as $key=>$value) { $user->{$key} = $value; } return $user; } /** * Called when the user record is updated. * Modifies user in external database. It takes olduser (before changes) and newuser (after changes) * compares information saved modified information to external db. * * @param stdClass $olduser Userobject before modifications * @param stdClass $newuser Userobject new modified userobject * @return boolean result * */ function user_update($olduser, $newuser) { if (isset($olduser->username) and isset($newuser->username) and $olduser->username != $newuser->username) { error_log("ERROR:User renaming not allowed in ext db"); return false; } if (isset($olduser->auth) and $olduser->auth != $this->authtype) { return true; // Just change auth and skip update. } $curruser = $this->get_userinfo($olduser->username); if (empty($curruser)) { error_log("ERROR:User $olduser->username found in ext db"); return false; } $extusername = core_text::convert($olduser->username, 'utf-8', $this->config->extencoding); $authdb = $this->db_init(); $update = array(); foreach($curruser as $key=>$value) { if ($key == 'username') { continue; // Skip this. } if (empty($this->config->{"field_updateremote_$key"})) { continue; // Remote update not requested. } if (!isset($newuser->$key)) { continue; } $nuvalue = $newuser->$key; // Support for textarea fields. if (isset($nuvalue['text'])) { $nuvalue = $nuvalue['text']; } if ($nuvalue != $value) { $update[] = $this->config->{"field_map_$key"}."='".$this->ext_addslashes(core_text::convert($nuvalue, 'utf-8', $this->config->extencoding))."'"; } } if (!empty($update)) { $sql = "UPDATE {$this->config->table} SET ".implode(',', $update)." WHERE {$this->config->fielduser} = ?"; if (!$authdb->Execute($sql, array($this->ext_addslashes($extusername)))) { throw new \moodle_exception('auth_dbupdateerror', 'auth_db'); } } $authdb->Close(); return true; } function prevent_local_passwords() { return !$this->is_internal(); } /** * Returns true if this authentication plugin is "internal". * * Internal plugins use password hashes from Moodle user table for authentication. * * @return bool */ function is_internal() { if (!isset($this->config->passtype)) { return true; } return ($this->config->passtype === 'internal'); } /** * Returns false if this plugin is enabled but not configured. * * @return bool */ public function is_configured() { if (!empty($this->config->type)) { return true; } return false; } /** * Indicates if moodle should automatically update internal user * records with data from external sources using the information * from auth_plugin_base::get_userinfo(). * * @return bool true means automatically copy data from ext to user table */ function is_synchronised_with_external() { return true; } /** * Returns true if this authentication plugin can change the user's * password. * * @return bool */ function can_change_password() { return ($this->is_internal() or !empty($this->config->changepasswordurl)); } /** * Returns the URL for changing the user's pw, or empty if the default can * be used. * * @return moodle_url */ function change_password_url() { if ($this->is_internal() || empty($this->config->changepasswordurl)) { // Standard form. return null; } else { // Use admin defined custom url. return new moodle_url($this->config->changepasswordurl); } } /** * Returns true if plugin allows resetting of internal password. * * @return bool */ function can_reset_password() { return $this->is_internal(); } /** * Add slashes, we can not use placeholders or system functions. * * @param string $text * @return string */ function ext_addslashes($text) { if (empty($this->config->sybasequoting)) { $text = str_replace('\\', '\\\\', $text); $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text); } else { $text = str_replace("'", "''", $text); } return $text; } /** * Test if settings are ok, print info to output. * @private */ public function test_settings() { global $CFG, $OUTPUT; // NOTE: this is not localised intentionally, admins are supposed to understand English at least a bit... raise_memory_limit(MEMORY_HUGE); if (empty($this->config->table)) { echo $OUTPUT->notification(get_string('auth_dbnoexttable', 'auth_db'), 'notifyproblem'); return; } if (empty($this->config->fielduser)) { echo $OUTPUT->notification(get_string('auth_dbnouserfield', 'auth_db'), 'notifyproblem'); return; } $olddebug = $CFG->debug; $olddisplay = ini_get('display_errors'); ini_set('display_errors', '1'); $CFG->debug = DEBUG_DEVELOPER; $olddebugauthdb = $this->config->debugauthdb; $this->config->debugauthdb = 1; error_reporting($CFG->debug); $adodb = $this->db_init(); if (!$adodb or !$adodb->IsConnected()) { $this->config->debugauthdb = $olddebugauthdb; $CFG->debug = $olddebug; ini_set('display_errors', $olddisplay); error_reporting($CFG->debug); ob_end_flush(); echo $OUTPUT->notification(get_string('auth_dbcannotconnect', 'auth_db'), 'notifyproblem'); return; } $rs = $adodb->Execute("SELECT * FROM {$this->config->table} WHERE {$this->config->fielduser} <> 'random_unlikely_username'"); // Any unlikely name is ok here. if (!$rs) { echo $OUTPUT->notification(get_string('auth_dbcannotreadtable', 'auth_db'), 'notifyproblem'); } else if ($rs->EOF) { echo $OUTPUT->notification(get_string('auth_dbtableempty', 'auth_db'), 'notifyproblem'); $rs->close(); } else { $columns = array_keys($rs->fetchRow()); echo $OUTPUT->notification(get_string('auth_dbcolumnlist', 'auth_db', implode(', ', $columns)), 'notifysuccess'); $rs->close(); } $adodb->Close(); $this->config->debugauthdb = $olddebugauthdb; $CFG->debug = $olddebug; ini_set('display_errors', $olddisplay); error_reporting($CFG->debug); ob_end_flush(); } /** * Clean the user data that comes from an external database. * @deprecated since 3.1, please use core_user::clean_data() instead. * @param array $user the user data to be validated against properties definition. * @return stdClass $user the cleaned user data. */ public function clean_data($user) { debugging('The method clean_data() has been deprecated, please use core_user::clean_data() instead.', DEBUG_DEVELOPER); return core_user::clean_data($user); } } settings.php000064400000014724152265520240007130 0ustar00. /** * Admin settings and defaults. * * @package auth_db * @copyright 2017 Stephen Bourget * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die; if ($ADMIN->fulltree) { // We use a couple of custom admin settings since we need to massage the data before it is inserted into the DB. require_once($CFG->dirroot.'/auth/db/classes/admin_setting_special_auth_configtext.php'); // Needed for constants. require_once($CFG->libdir.'/authlib.php'); // Introductory explanation. $settings->add(new admin_setting_heading('auth_db/pluginname', '', new lang_string('auth_dbdescription', 'auth_db'))); // Host. $settings->add(new admin_setting_configtext('auth_db/host', get_string('auth_dbhost_key', 'auth_db'), get_string('auth_dbhost', 'auth_db') . ' ' .get_string('auth_multiplehosts', 'auth'), '127.0.0.1', PARAM_RAW)); // Type. $dboptions = array(); $dbtypes = array("access", "ado_access", "ado", "ado_mssql", "borland_ibase", "csv", "db2", "fbsql", "firebird", "ibase", "informix72", "informix", "mssql", "mssql_n", "mssqlnative", "mysql", "mysqli", "mysqlt", "oci805", "oci8", "oci8po", "odbc", "odbc_mssql", "odbc_oracle", "oracle", "pdo", "postgres64", "postgres7", "postgres", "proxy", "sqlanywhere", "sybase", "vfp"); foreach ($dbtypes as $dbtype) { $dboptions[$dbtype] = $dbtype; } $settings->add(new admin_setting_configselect('auth_db/type', new lang_string('auth_dbtype_key', 'auth_db'), new lang_string('auth_dbtype', 'auth_db'), 'mysqli', $dboptions)); // Sybase quotes. $yesno = array( new lang_string('no'), new lang_string('yes'), ); $settings->add(new admin_setting_configselect('auth_db/sybasequoting', new lang_string('auth_dbsybasequoting', 'auth_db'), new lang_string('auth_dbsybasequotinghelp', 'auth_db'), 0, $yesno)); // DB Name. $settings->add(new admin_setting_configtext('auth_db/name', get_string('auth_dbname_key', 'auth_db'), get_string('auth_dbname', 'auth_db'), '', PARAM_RAW_TRIMMED)); // DB Username. $settings->add(new admin_setting_configtext('auth_db/user', get_string('auth_dbuser_key', 'auth_db'), get_string('auth_dbuser', 'auth_db'), '', PARAM_RAW_TRIMMED)); // Password. $settings->add(new admin_setting_configpasswordunmask('auth_db/pass', get_string('auth_dbpass_key', 'auth_db'), get_string('auth_dbpass', 'auth_db'), '')); // DB Table. $settings->add(new admin_setting_configtext('auth_db/table', get_string('auth_dbtable_key', 'auth_db'), get_string('auth_dbtable', 'auth_db'), '', PARAM_RAW_TRIMMED)); // DB User field. $settings->add(new admin_setting_configtext('auth_db/fielduser', get_string('auth_dbfielduser_key', 'auth_db'), get_string('auth_dbfielduser', 'auth_db'), '', PARAM_RAW_TRIMMED)); // DB User password. $settings->add(new admin_setting_configtext('auth_db/fieldpass', get_string('auth_dbfieldpass_key', 'auth_db'), get_string('auth_dbfieldpass', 'auth_db'), '', PARAM_RAW_TRIMMED)); // DB Password Type. $passtype = array(); $passtype["plaintext"] = get_string("plaintext", "auth"); $passtype["md5"] = get_string("md5", "auth"); $passtype["sha1"] = get_string("sha1", "auth"); $passtype["saltedcrypt"] = get_string("auth_dbsaltedcrypt", "auth_db"); $passtype["internal"] = get_string("internal", "auth"); $settings->add(new admin_setting_configselect('auth_db/passtype', new lang_string('auth_dbpasstype_key', 'auth_db'), new lang_string('auth_dbpasstype', 'auth_db'), 'plaintext', $passtype)); // Encoding. $settings->add(new admin_setting_configtext('auth_db/extencoding', get_string('auth_dbextencoding', 'auth_db'), get_string('auth_dbextencodinghelp', 'auth_db'), 'utf-8', PARAM_RAW_TRIMMED)); // DB SQL SETUP. $settings->add(new admin_setting_configtext('auth_db/setupsql', get_string('auth_dbsetupsql', 'auth_db'), get_string('auth_dbsetupsqlhelp', 'auth_db'), '', PARAM_RAW_TRIMMED)); // Debug ADOOB. $settings->add(new admin_setting_configselect('auth_db/debugauthdb', new lang_string('auth_dbdebugauthdb', 'auth_db'), new lang_string('auth_dbdebugauthdbhelp', 'auth_db'), 0, $yesno)); // Password change URL. $settings->add(new auth_db_admin_setting_special_auth_configtext('auth_db/changepasswordurl', get_string('auth_dbchangepasswordurl_key', 'auth_db'), get_string('changepasswordhelp', 'auth'), '', PARAM_URL)); // Label and Sync Options. $settings->add(new admin_setting_heading('auth_db/usersync', new lang_string('auth_sync_script', 'auth'), '')); // Sync Options. $deleteopt = array(); $deleteopt[AUTH_REMOVEUSER_KEEP] = get_string('auth_remove_keep', 'auth'); $deleteopt[AUTH_REMOVEUSER_SUSPEND] = get_string('auth_remove_suspend', 'auth'); $deleteopt[AUTH_REMOVEUSER_FULLDELETE] = get_string('auth_remove_delete', 'auth'); $settings->add(new admin_setting_configselect('auth_db/removeuser', new lang_string('auth_remove_user_key', 'auth'), new lang_string('auth_remove_user', 'auth'), AUTH_REMOVEUSER_KEEP, $deleteopt)); // Update users. $settings->add(new admin_setting_configselect('auth_db/updateusers', new lang_string('auth_dbupdateusers', 'auth_db'), new lang_string('auth_dbupdateusers_description', 'auth_db'), 0, $yesno)); // Display locking / mapping of profile fields. $authplugin = get_auth_plugin('db'); display_auth_lock_options($settings, $authplugin->authtype, $authplugin->userfields, get_string('auth_dbextrafields', 'auth_db'), true, true, $authplugin->get_custom_user_profile_fields()); } renamedclasses.php000064400000004252152266350120010253 0ustar00. /** * This file contains mappings for classes that have been renamed so that they meet the requirements of the autoloader. * * Renaming isn't always the recommended approach, but can provide benefit in situations where we've already got a * close structure, OR where lots of classes get included and not necessarily used, or checked for often. * * When renaming a class delete the original class and add an entry to the db/renamedclasses.php directory for that * component. * This way we don't need to keep around old classes, instead creating aliases only when required. * One big advantage to this method is that we provide consistent debugging for renamed classes when they are used. * * @package core * @copyright 2014 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); // Like other files in the db directory this file uses an array. // The old class name is the key, the new class name is the value. // The array must be called $renamedclasses. $renamedclasses = [ // Since Moodle 4.5. 'core_reportbuilder\\report_access_exception' => 'core_reportbuilder\\exception\\report_access_exception', 'core_reportbuilder\\source_invalid_exception' => 'core_reportbuilder\\exception\\source_invalid_exception', 'core_reportbuilder\\source_unavailable_exception' => 'core_reportbuilder\\exception\\source_unavailable_exception', // Since Moodle 5.0. 'PNG_MetaDataHandler' => 'core_badges\\png_metadata_handler', ]; services.php000064400000433507152266351300007117 0ustar00. /** * Core external functions and service definitions. * * The functions and services defined on this file are * processed and registered into the Moodle DB after any * install or upgrade operation. All plugins support this. * * For more information, take a look to the documentation available: * - Webservices API: {@link https://moodledev.io/docs/apis/subsystems/external/writing-a-service} * - External API: {@link https://moodledev.io/docs/apis/subsystems/external/functions} * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @package core_webservice * @category webservice * @copyright 2009 Petr Skodak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $functions = array( 'core_auth_confirm_user' => array( 'classname' => 'core_auth_external', 'methodname' => 'confirm_user', 'description' => 'Confirm a user account.', 'type' => 'write', 'ajax' => true, 'loginrequired' => false, ), 'core_auth_request_password_reset' => array( 'classname' => 'core_auth_external', 'methodname' => 'request_password_reset', 'description' => 'Requests a password reset.', 'type' => 'write', 'ajax' => true, 'loginrequired' => false, ), 'core_auth_is_minor' => array( 'classname' => 'core_auth_external', 'methodname' => 'is_minor', 'description' => 'Requests a check if a user is a digital minor.', 'type' => 'read', 'ajax' => true, 'loginrequired' => false, ), 'core_auth_is_age_digital_consent_verification_enabled' => array( 'classname' => 'core_auth_external', 'methodname' => 'is_age_digital_consent_verification_enabled', 'description' => 'Checks if age digital consent verification is enabled.', 'type' => 'read', 'ajax' => true, 'loginrequired' => false, ), 'core_auth_resend_confirmation_email' => array( 'classname' => 'core_auth_external', 'methodname' => 'resend_confirmation_email', 'description' => 'Resend confirmation email.', 'type' => 'write', 'ajax' => true, 'loginrequired' => false, ), 'core_backup_get_async_backup_progress' => array( 'classname' => 'core_backup_external', 'classpath' => 'backup/externallib.php', 'methodname' => 'get_async_backup_progress', 'description' => 'Get the progress of an Asyncronhous backup.', 'type' => 'read', 'ajax' => true, 'loginrequired' => true, ), 'core_backup_get_async_backup_links_backup' => array( 'classname' => 'core_backup_external', 'classpath' => 'backup/externallib.php', 'methodname' => 'get_async_backup_links_backup', 'description' => 'Gets the data to use when updating the status table row in the UI for when an async backup completes.', 'type' => 'read', 'ajax' => true, 'loginrequired' => true, ), 'core_backup_get_async_backup_links_restore' => array( 'classname' => 'core_backup_external', 'classpath' => 'backup/externallib.php', 'methodname' => 'get_async_backup_links_restore', 'description' => 'Gets the data to use when updating the status table row in the UI for when an async restore completes.', 'type' => 'read', 'ajax' => true, 'loginrequired' => true, ), 'core_backup_get_copy_progress' => array( 'classname' => 'core_backup_external', 'classpath' => 'backup/externallib.php', 'methodname' => 'get_copy_progress', 'description' => 'Gets the progress of course copy operations.', 'type' => 'read', 'ajax' => true, 'loginrequired' => true, ), 'core_backup_submit_copy_form' => array( 'classname' => 'core_backup_external', 'classpath' => 'backup/externallib.php', 'methodname' => 'submit_copy_form', 'description' => 'Handles ajax submission of course copy form.', 'type' => 'read', 'ajax' => true, 'loginrequired' => true, ), 'core_badges_disable_badges' => [ 'classname' => 'core_badges\external\disable_badges', 'description' => 'Disable badges', 'type' => 'write', 'ajax' => true, ], 'core_badges_enable_badges' => [ 'classname' => 'core_badges\external\enable_badges', 'description' => 'Enable badges', 'type' => 'write', 'ajax' => true, ], 'core_badges_get_badge' => [ 'classname' => 'core_badges\external\get_badge', 'description' => 'Retrieves a badge by id.', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_badges_get_user_badges' => array( 'classname' => 'core_badges_external', 'methodname' => 'get_user_badges', 'description' => 'Returns the list of badges awarded to a user.', 'type' => 'read', 'capabilities' => 'moodle/badges:viewotherbadges', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_badges_get_user_badge_by_hash' => [ 'classname' => 'core_badges\external\get_user_badge_by_hash', 'description' => 'Returns the badge awarded to a user by hash.', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_blog_get_entries' => array( 'classname' => 'core_blog\external', 'methodname' => 'get_entries', 'description' => 'Returns blog entries.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, 'loginrequired' => false, ), 'core_blog_view_entries' => array( 'classname' => 'core_blog\external', 'methodname' => 'view_entries', 'description' => 'Trigger the blog_entries_viewed event.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, 'loginrequired' => false, ), 'core_blog_get_access_information' => [ 'classname' => '\core_blog\external\get_access_information', 'description' => 'Retrieves permission information for the current user.', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_blog_add_entry' => [ 'classname' => '\core_blog\external\add_entry', 'description' => 'Creates a new blog post entry.', 'type' => 'write', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_blog_delete_entry' => [ 'classname' => '\core_blog\external\delete_entry', 'description' => 'Deletes a blog post entry.', 'type' => 'write', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_blog_prepare_entry_for_edition' => [ 'classname' => '\core_blog\external\prepare_entry_for_edition', 'description' => 'Prepare a draft area for editing a blog entry..', 'type' => 'write', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_blog_update_entry' => [ 'classname' => '\core_blog\external\update_entry', 'description' => 'Updates a blog entry.', 'type' => 'write', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_calendar_get_calendar_monthly_view' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_monthly_view', 'description' => 'Fetch the monthly view data for a calendar', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => '', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_calendar_day_view' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_day_view', 'description' => 'Fetch the day view data for a calendar', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => '', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_calendar_upcoming_view' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_upcoming_view', 'description' => 'Fetch the upcoming view data for a calendar', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => '', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_update_event_start_day' => array( 'classname' => 'core_calendar_external', 'methodname' => 'update_event_start_day', 'description' => 'Update the start day (but not time) for an event.', 'classpath' => 'calendar/externallib.php', 'type' => 'write', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_create_calendar_events' => array( 'classname' => 'core_calendar_external', 'methodname' => 'create_calendar_events', 'description' => 'Create calendar events', 'classpath' => 'calendar/externallib.php', 'type' => 'write', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_delete_calendar_events' => array( 'classname' => 'core_calendar_external', 'methodname' => 'delete_calendar_events', 'description' => 'Delete calendar events', 'classpath' => 'calendar/externallib.php', 'type' => 'write', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_calendar_events' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_events', 'description' => 'Get calendar events', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_action_events_by_timesort' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_action_events_by_timesort', 'description' => 'Get calendar action events by tiemsort', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'readonlysession' => true, ), 'core_calendar_get_action_events_by_course' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_action_events_by_course', 'description' => 'Get calendar action events by course', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_action_events_by_courses' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_action_events_by_courses', 'description' => 'Get calendar action events by courses', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_calendar_event_by_id' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_event_by_id', 'description' => 'Get calendar event by id', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_submit_create_update_form' => array( 'classname' => 'core_calendar_external', 'methodname' => 'submit_create_update_form', 'description' => 'Submit form data for event form', 'classpath' => 'calendar/externallib.php', 'type' => 'write', 'capabilities' => 'moodle/calendar:manageentries, moodle/calendar:manageownentries, moodle/calendar:managegroupentries', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_calendar_access_information' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_calendar_access_information', 'description' => 'Convenience function to retrieve some permissions/access information for the given course calendar.', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_allowed_event_types' => array( 'classname' => 'core_calendar_external', 'methodname' => 'get_allowed_event_types', 'description' => 'Get the type of events a user can create in the given course.', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_calendar_get_timestamps' => [ 'classname' => 'core_calendar_external', 'methodname' => 'get_timestamps', 'description' => 'Fetch unix timestamps for given date times.', 'classpath' => 'calendar/externallib.php', 'type' => 'read', 'ajax' => true, ], 'core_calendar_get_calendar_export_token' => [ 'classname' => 'core_calendar\external\export\token', 'description' => 'Return the auth token required for exporting a calendar.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ], 'core_calendar_delete_subscription' => [ 'classname' => 'core_calendar\external\subscription\delete', 'description' => 'Delete the calendar subscription', 'type' => 'write', 'ajax' => true ], 'core_check_get_result_admintree' => [ 'classname' => 'core\check\external\get_result_admintree', 'description' => 'Executes a check stored in the admin tree and returns the result', 'type' => 'read', 'ajax' => true, 'readonlysession' => true, ], 'core_cohort_add_cohort_members' => array( 'classname' => 'core_cohort_external', 'methodname' => 'add_cohort_members', 'classpath' => 'cohort/externallib.php', 'description' => 'Adds cohort members.', 'type' => 'write', 'capabilities' => 'moodle/cohort:assign' ), 'core_cohort_create_cohorts' => array( 'classname' => 'core_cohort_external', 'methodname' => 'create_cohorts', 'classpath' => 'cohort/externallib.php', 'description' => 'Creates new cohorts.', 'type' => 'write', 'capabilities' => 'moodle/cohort:manage' ), 'core_cohort_delete_cohort_members' => array( 'classname' => 'core_cohort_external', 'methodname' => 'delete_cohort_members', 'classpath' => 'cohort/externallib.php', 'description' => 'Deletes cohort members.', 'type' => 'write', 'capabilities' => 'moodle/cohort:assign' ), 'core_cohort_delete_cohorts' => array( 'classname' => 'core_cohort_external', 'methodname' => 'delete_cohorts', 'classpath' => 'cohort/externallib.php', 'description' => 'Deletes all specified cohorts.', 'type' => 'write', 'capabilities' => 'moodle/cohort:manage', 'ajax' => true, ), 'core_cohort_get_cohort_members' => array( 'classname' => 'core_cohort_external', 'methodname' => 'get_cohort_members', 'classpath' => 'cohort/externallib.php', 'description' => 'Returns cohort members.', 'type' => 'read', 'capabilities' => 'moodle/cohort:view' ), 'core_cohort_search_cohorts' => array( 'classname' => 'core_cohort_external', 'methodname' => 'search_cohorts', 'classpath' => 'cohort/externallib.php', 'description' => 'Search for cohorts.', 'type' => 'read', 'ajax' => true, 'capabilities' => 'moodle/cohort:view' ), 'core_cohort_get_cohorts' => array( 'classname' => 'core_cohort_external', 'methodname' => 'get_cohorts', 'classpath' => 'cohort/externallib.php', 'description' => 'Returns cohort details.', 'type' => 'read', 'capabilities' => 'moodle/cohort:view' ), 'core_cohort_update_cohorts' => array( 'classname' => 'core_cohort_external', 'methodname' => 'update_cohorts', 'classpath' => 'cohort/externallib.php', 'description' => 'Updates existing cohorts.', 'type' => 'write', 'capabilities' => 'moodle/cohort:manage' ), 'core_comment_get_comments' => array( 'classname' => 'core_comment_external', 'methodname' => 'get_comments', 'description' => 'Returns comments.', 'type' => 'read', 'capabilities' => 'moodle/comment:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_comment_add_comments' => array( 'classname' => 'core_comment_external', 'methodname' => 'add_comments', 'description' => 'Adds a comment or comments.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_comment_delete_comments' => array( 'classname' => 'core_comment_external', 'methodname' => 'delete_comments', 'description' => 'Deletes a comment or comments.', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_completion_get_activities_completion_status' => array( 'classname' => 'core_completion_external', 'methodname' => 'get_activities_completion_status', 'description' => 'Return the activities completion status for a user in a course.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_completion_get_course_completion_status' => array( 'classname' => 'core_completion_external', 'methodname' => 'get_course_completion_status', 'description' => 'Returns course completion status.', 'type' => 'read', 'capabilities' => 'report/completion:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_completion_mark_course_self_completed' => array( 'classname' => 'core_completion_external', 'methodname' => 'mark_course_self_completed', 'description' => 'Update the course completion status for the current user (if course self-completion is enabled).', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_completion_update_activity_completion_status_manually' => array( 'classname' => 'core_completion_external', 'methodname' => 'update_activity_completion_status_manually', 'description' => 'Update completion status for the current user in an activity, only for activities with manual tracking.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_completion_override_activity_completion_status' => array( 'classname' => 'core_completion_external', 'methodname' => 'override_activity_completion_status', 'description' => 'Update completion status for a user in an activity by overriding it.', 'type' => 'write', 'capabilities' => 'moodle/course:overridecompletion', 'ajax' => true, ), 'core_course_create_categories' => array( 'classname' => 'core_course_external', 'methodname' => 'create_categories', 'classpath' => 'course/externallib.php', 'description' => 'Create course categories', 'type' => 'write', 'capabilities' => 'moodle/category:manage' ), 'core_course_create_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'create_courses', 'classpath' => 'course/externallib.php', 'description' => 'Create new courses', 'type' => 'write', 'capabilities' => 'moodle/course:create, moodle/course:visibility' ), 'core_course_delete_categories' => array( 'classname' => 'core_course_external', 'methodname' => 'delete_categories', 'classpath' => 'course/externallib.php', 'description' => 'Delete course categories', 'type' => 'write', 'capabilities' => 'moodle/category:manage' ), 'core_course_delete_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'delete_courses', 'classpath' => 'course/externallib.php', 'description' => 'Deletes all specified courses', 'type' => 'write', 'capabilities' => 'moodle/course:delete' ), 'core_course_delete_modules' => array( 'classname' => 'core_course_external', 'methodname' => 'delete_modules', 'classpath' => 'course/externallib.php', 'description' => 'Deletes all specified module instances', 'type' => 'write', 'capabilities' => 'moodle/course:manageactivities' ), 'core_course_duplicate_course' => array( 'classname' => 'core_course_external', 'methodname' => 'duplicate_course', 'classpath' => 'course/externallib.php', 'description' => 'Duplicate an existing course (creating a new one).', 'type' => 'write', 'capabilities' => 'moodle/backup:backupcourse, moodle/restore:restorecourse, moodle/course:create' ), 'core_course_get_categories' => array( 'classname' => 'core_course_external', 'methodname' => 'get_categories', 'classpath' => 'course/externallib.php', 'description' => 'Return category details', 'type' => 'read', 'capabilities' => 'moodle/category:viewhiddencategories', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_contents' => array( 'classname' => 'core_course_external', 'methodname' => 'get_course_contents', 'classpath' => 'course/externallib.php', 'description' => 'Get course contents', 'type' => 'read', 'capabilities' => 'moodle/course:update, moodle/course:viewhiddencourses', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_course_module' => array( 'classname' => 'core_course_external', 'methodname' => 'get_course_module', 'classpath' => 'course/externallib.php', 'description' => 'Return information about a course module', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_course_module_by_instance' => array( 'classname' => 'core_course_external', 'methodname' => 'get_course_module_by_instance', 'classpath' => 'course/externallib.php', 'description' => 'Return information about a given module name and instance id', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), // Todo Remove this entry in Moodle 6.0 (MDL-83530). 'core_course_get_module' => array( 'classname' => 'core_course_external', 'methodname' => 'get_module', 'classpath' => 'course/externallib.php', 'description' => '** DEPRECATED ** Please do not call this function any more (will be removed in Moodle 6.0).' . 'Returns html with one activity module on course page.' . 'Use fragment API using component core_courseformat and fragment cmitem instead.', 'type' => 'read', 'ajax' => true, ), 'core_courseformat_file_handlers' => [ 'classname' => 'core_courseformat\external\file_handlers', 'description' => 'Get the current course file hanlders.', 'type' => 'read', 'ajax' => true, ], 'core_courseformat_get_state' => [ 'classname' => 'core_courseformat\external\get_state', 'description' => 'Get the current course state.', 'type' => 'read', 'ajax' => true, ], 'core_courseformat_new_module' => [ 'classname' => 'core_courseformat\external\new_module', 'methodname' => 'execute', 'description' => 'Create a new module to course.', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/course:manageactivities', ], 'core_courseformat_update_course' => [ 'classname' => 'core_courseformat\external\update_course', 'methodname' => 'execute', 'description' => 'Update course contents.', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/course:sectionvisibility, moodle/course:activityvisibility', ], 'core_courseformat_create_module' => [ 'classname' => 'core_courseformat\external\create_module', 'methodname' => 'execute', 'description' => '** DEPRECATED ** Please do not call this function any more. ' . 'Use core_courseformat_new_module instead. ' . 'Add module to course.', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/course:manageactivities', ], // Todo Remove this entry in Moodle 6.0 (MDL-83530). 'core_course_edit_module' => array( 'classname' => 'core_course_external', 'methodname' => 'edit_module', 'classpath' => 'course/externallib.php', 'description' => '** DEPRECATED ** Please do not call this function any more (will be removed in Moodle 6.0).' . ' Performs an action on course module (change visibility, duplicate, delete, etc.)' . ' Use core_courseformat_update_course instead.', 'type' => 'write', 'ajax' => true, ), // Todo Remove this entry in Moodle 6.0 (MDL-83530). 'core_course_edit_section' => array( 'classname' => 'core_course_external', 'methodname' => 'edit_section', 'classpath' => 'course/externallib.php', 'description' => '** DEPRECATED ** Please do not call this function any more (will be removed in Moodle 6.0).' . 'Performs an action on course section (change visibility, set marker, delete)' . ' Use core_courseformat_update_course instead.', 'type' => 'write', 'ajax' => true, ), 'core_course_get_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'get_courses', 'classpath' => 'course/externallib.php', 'description' => 'Return course details', 'type' => 'read', 'capabilities' => 'moodle/course:view, moodle/course:update, moodle/course:viewhiddencourses', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_import_course' => array( 'classname' => 'core_course_external', 'methodname' => 'import_course', 'classpath' => 'course/externallib.php', 'description' => 'Import course data from a course into another course. Does not include any user data.', 'type' => 'write', 'capabilities' => 'moodle/backup:backuptargetimport, moodle/restore:restoretargetimport' ), 'core_course_search_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'search_courses', 'classpath' => 'course/externallib.php', 'description' => 'Search courses by (name, module, block, tag)', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_update_categories' => array( 'classname' => 'core_course_external', 'methodname' => 'update_categories', 'classpath' => 'course/externallib.php', 'description' => 'Update categories', 'type' => 'write', 'capabilities' => 'moodle/category:manage', ), 'core_course_update_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'update_courses', 'classpath' => 'course/externallib.php', 'description' => 'Update courses', 'type' => 'write', 'capabilities' => 'moodle/course:update, moodle/course:changecategory, moodle/course:changefullname, ' . 'moodle/course:changeshortname, moodle/course:changeidnumber, moodle/course:changesummary, moodle/course:visibility' ), 'core_course_view_course' => array( 'classname' => 'core_course_external', 'methodname' => 'view_course', 'classpath' => 'course/externallib.php', 'description' => 'Log that the course was viewed', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_user_navigation_options' => array( 'classname' => 'core_course_external', 'methodname' => 'get_user_navigation_options', 'classpath' => 'course/externallib.php', 'description' => 'Return a list of navigation options in a set of courses that are avaialable or not for the current user.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_user_administration_options' => array( 'classname' => 'core_course_external', 'methodname' => 'get_user_administration_options', 'classpath' => 'course/externallib.php', 'description' => 'Return a list of administration options in a set of courses that are avaialable or not for the current user.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_courses_by_field' => array( 'classname' => 'core_course_external', 'methodname' => 'get_courses_by_field', 'classpath' => 'course/externallib.php', 'description' => 'Get courses matching a specific field (id/s, shortname, idnumber, category)', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_check_updates' => array( 'classname' => 'core_course_external', 'methodname' => 'check_updates', 'classpath' => 'course/externallib.php', 'description' => 'Check if there is updates affecting the user for the given course and contexts.', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_updates_since' => array( 'classname' => 'core_course_external', 'methodname' => 'get_updates_since', 'classpath' => 'course/externallib.php', 'description' => 'Check if there are updates affecting the user for the given course since the given time stamp.', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_enrolled_courses_by_timeline_classification' => array( 'classname' => 'core_course_external', 'methodname' => 'get_enrolled_courses_by_timeline_classification', 'classpath' => 'course/externallib.php', 'description' => 'List of enrolled courses for the given timeline classification (past, inprogress, or future).', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_enrolled_courses_with_action_events_by_timeline_classification' => array( 'classname' => '\core_course\external\get_enrolled_courses_with_action_events_by_timeline_classification', 'methodname' => 'execute', 'classpath' => '', 'description' => 'List of enrolled courses with action events in a given timeframe, for the given timeline classification.', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'readonlysession' => true, ), 'core_course_get_recent_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'get_recent_courses', 'classpath' => 'course/externallib.php', 'description' => 'List of courses a user has accessed most recently.', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_set_favourite_courses' => array( 'classname' => 'core_course_external', 'methodname' => 'set_favourite_courses', 'classpath' => 'course/externallib.php', 'description' => 'Add a list of courses to the list of favourite courses.', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_course_get_enrolled_users_by_cmid' => array( 'classname' => 'core_course_external', 'methodname' => 'get_enrolled_users_by_cmid', 'classpath' => 'course/externallib.php', 'description' => 'List users by course module id, filter by group and active enrolment status.', 'type' => 'read', 'ajax' => true, ), 'core_course_add_content_item_to_user_favourites' => array( 'classname' => 'core_course_external', 'methodname' => 'add_content_item_to_user_favourites', 'classpath' => 'course/externallib.php', 'description' => 'Adds a content item (activity, resource or their subtypes) to the favourites for the user.', 'type' => 'write', 'ajax' => true, ), 'core_course_remove_content_item_from_user_favourites' => array( 'classname' => 'core_course_external', 'methodname' => 'remove_content_item_from_user_favourites', 'classpath' => 'course/externallib.php', 'description' => 'Removes a content item (activity, resource or their subtypes) from the favourites for the user.', 'type' => 'write', 'ajax' => true, ), 'core_course_get_course_content_items' => array( 'classname' => 'core_course_external', 'methodname' => 'get_course_content_items', 'classpath' => 'course/externallib.php', 'description' => 'Fetch all the content items (activities, resources and their subtypes) for the activity picker', 'type' => 'read', 'ajax' => true, ), 'core_course_get_activity_chooser_footer' => array( 'classname' => 'core_course_external', 'methodname' => 'get_activity_chooser_footer', 'classpath' => 'course/externallib.php', 'description' => 'Fetch the data for the activity chooser footer.', 'type' => 'read', 'ajax' => true, ), 'core_course_toggle_activity_recommendation' => array( 'classname' => 'core_course_external', 'methodname' => 'toggle_activity_recommendation', 'classpath' => 'course/externallib.php', 'description' => 'Adds or removes an activity as a recommendation in the activity chooser.', 'type' => 'write', 'ajax' => true, ), 'core_enrol_get_course_enrolment_methods' => array( 'classname' => 'core_enrol_external', 'methodname' => 'get_course_enrolment_methods', 'classpath' => 'enrol/externallib.php', 'description' => 'Get the list of course enrolment methods', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_enrol_get_enrolled_users' => array( 'classname' => 'core_enrol_external', 'methodname' => 'get_enrolled_users', 'classpath' => 'enrol/externallib.php', 'description' => 'Get enrolled users by course id.', 'type' => 'read', 'capabilities' => 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update, ' . 'moodle/site:accessallgroups', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_enrol_get_enrolled_users_with_capability' => array( 'classname' => 'core_enrol_external', 'methodname' => 'get_enrolled_users_with_capability', 'classpath' => 'enrol/externallib.php', 'description' => 'For each course and capability specified, return a list of the users that are enrolled in the course and have that capability', 'type' => 'read', ), 'core_enrol_get_potential_users' => array( 'classname' => 'core_enrol_external', 'methodname' => 'get_potential_users', 'classpath' => 'enrol/externallib.php', 'description' => 'Get the list of potential users to enrol', 'ajax' => true, 'type' => 'read', 'capabilities' => 'moodle/course:enrolreview', 'readonlysession' => true, ), 'core_enrol_search_users' => [ 'classname' => 'core_enrol_external', 'methodname' => 'search_users', 'classpath' => 'enrol/externallib.php', 'description' => 'Search within the list of course participants', 'ajax' => true, 'type' => 'read', 'capabilities' => 'moodle/course:viewparticipants', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ], 'core_enrol_get_users_courses' => array( 'classname' => 'core_enrol_external', 'methodname' => 'get_users_courses', 'classpath' => 'enrol/externallib.php', 'description' => 'Get the list of courses where a user is enrolled in', 'type' => 'read', 'capabilities' => 'moodle/course:viewparticipants', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_enrol_submit_user_enrolment_form' => array( 'classname' => 'core_enrol_external', 'methodname' => 'submit_user_enrolment_form', 'classpath' => 'enrol/externallib.php', 'description' => 'Submit form data for enrolment form', 'type' => 'write', 'ajax' => true, ), 'core_enrol_unenrol_user_enrolment' => array( 'classname' => 'core_enrol_external', 'methodname' => 'unenrol_user_enrolment', 'classpath' => 'enrol/externallib.php', 'description' => 'External function that unenrols a given user enrolment', 'type' => 'write', 'ajax' => true, ), 'core_fetch_notifications' => array( 'classname' => 'core_external', 'methodname' => 'fetch_notifications', 'classpath' => 'lib/external/externallib.php', 'description' => 'Return a list of notifications for the current session', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, 'readonlysession' => false, // Fetching removes from stack. ), 'core_session_touch' => array( 'classname' => 'core\session\external', 'methodname' => 'touch_session', 'description' => 'Keep the users session alive', 'type' => 'read', 'loginrequired' => true, 'ajax' => true, ), 'core_session_time_remaining' => array( 'classname' => 'core\session\external', 'methodname' => 'time_remaining', 'description' => 'Count the seconds remaining in this session', 'type' => 'read', 'loginrequired' => true, 'ajax' => true, ), 'core_files_get_files' => array( 'classname' => 'core_files_external', 'methodname' => 'get_files', 'description' => 'browse moodle files', 'type' => 'read', 'classpath' => 'files/externallib.php', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_files_upload' => array( 'classname' => 'core_files_external', 'methodname' => 'upload', 'description' => 'upload a file to moodle', 'type' => 'write', 'classpath' => 'files/externallib.php', ), 'core_files_delete_draft_files' => array( 'classname' => 'core_files\external\delete\draft', 'description' => 'Delete the indicated files (or directories) from a user draft file area.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_files_get_unused_draft_itemid' => array( 'classname' => 'core_files\external\get\unused_draft', 'description' => 'Generate a new draft itemid for the current user.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_form_get_filetypes_browser_data' => array( 'classname' => 'core_form\external', 'methodname' => 'get_filetypes_browser_data', 'classpath' => '', 'description' => 'Provides data for the filetypes element browser.', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, ), 'core_form_dynamic_form' => array( 'classname' => 'core_form\external\dynamic_form', 'description' => 'Process submission of a dynamic (modal) form', 'type' => 'write', 'ajax' => true, ), 'core_get_component_strings' => array( 'classname' => 'core_external', 'methodname' => 'get_component_strings', 'classpath' => 'lib/external/externallib.php', 'description' => 'Return all raw strings (with {$a->xxx}), for a specific component ' . '- similar to core get_component_strings(), call', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_get_fragment' => array( 'classname' => 'core_external', 'methodname' => 'get_fragment', 'classpath' => 'lib/external/externallib.php', 'description' => 'Return a fragment for inclusion, such as a JavaScript page.', 'type' => 'read', 'ajax' => true, ), 'core_get_string' => array( 'classname' => 'core_external', 'methodname' => 'get_string', 'classpath' => 'lib/external/externallib.php', 'description' => 'Return a translated string - similar to core get_string(), call', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, ), 'core_get_strings' => array( 'classname' => 'core_external', 'methodname' => 'get_strings', 'classpath' => 'lib/external/externallib.php', 'description' => 'Return some translated strings - like several core get_string(), calls', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, ), 'core_get_user_dates' => array( 'classname' => 'core_external', 'methodname' => 'get_user_dates', 'classpath' => 'lib/external/externallib.php', 'description' => 'Return formatted timestamps', 'type' => 'read', 'ajax' => true, ), 'core_grades_update_grades' => array( 'classname' => 'core_grades_external', 'methodname' => 'update_grades', 'description' => 'Update a grade item and associated student grades.', 'type' => 'write', ), 'core_grades_grader_gradingpanel_point_fetch' => [ 'classname' => 'core_grades\\grades\\grader\\gradingpanel\\point\\external\\fetch', 'description' => 'Fetch the data required to display the grader grading panel for simple grading, ' . 'creating the grade item if required', 'type' => 'write', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grades_grader_gradingpanel_point_store' => [ 'classname' => 'core_grades\\grades\\grader\\gradingpanel\\point\\external\\store', 'description' => 'Store the data required to display the grader grading panel for simple grading', 'type' => 'write', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grades_grader_gradingpanel_scale_fetch' => [ 'classname' => 'core_grades\\grades\\grader\\gradingpanel\\scale\\external\\fetch', 'description' => 'Fetch the data required to display the grader grading panel for scale-based grading, ' . 'creating the grade item if required', 'type' => 'write', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grades_grader_gradingpanel_scale_store' => [ 'classname' => 'core_grades\\grades\\grader\\gradingpanel\\scale\\external\\store', 'description' => 'Store the data required to display the grader grading panel for scale-based grading', 'type' => 'write', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grades_create_gradecategories' => array ( 'classname' => 'core_grades\external\create_gradecategories', 'description' => 'Create grade categories inside a course gradebook.', 'type' => 'write', 'capabilities' => 'moodle/grade:manage', ), 'core_grades_get_enrolled_users_for_selector' => array ( 'classname' => 'core_grades\external\get_enrolled_users_for_selector', 'description' => 'Returns the enrolled users within and map some fields to the returned array of user objects.', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ), 'core_grades_get_groups_for_selector' => [ 'classname' => 'core_group\external\get_groups_for_selector', 'description' => '** DEPRECATED ** Please do not call this function any more. ' . 'Use core_group_get_groups_for_selector instead. ' . 'Get the group/(s) for a course', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grades_get_feedback' => [ 'classname' => 'core_grades\external\get_feedback', 'description' => 'Get the feedback data for a grade item', 'type' => 'read', 'ajax' => true, ], 'core_grades_get_gradeitems' => [ 'classname' => 'core_grades\external\get_gradeitems', 'description' => 'Get the gradeitems for a course', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grades_get_grade_tree' => [ 'classname' => 'core_grades\external\get_grade_tree', 'description' => 'Get the grade tree structure for a course', 'type' => 'read', 'ajax' => true, ], 'core_grades_get_gradable_users' => [ 'classname' => 'core_grades\external\get_gradable_users', 'description' => 'Returns the gradable users in a course', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_grading_get_definitions' => array( 'classname' => 'core_grading_external', 'methodname' => 'get_definitions', 'description' => 'Get grading definitions', 'type' => 'read', ), 'core_grading_get_gradingform_instances' => array( 'classname' => 'core_grading_external', 'methodname' => 'get_gradingform_instances', 'description' => 'Get grading form instances', 'type' => 'read', ), 'core_grading_save_definitions' => array( 'classname' => 'core_grading_external', 'methodname' => 'save_definitions', 'description' => 'Save grading definitions', 'type' => 'write', ), 'core_group_add_group_members' => array( 'classname' => 'core_group_external', 'methodname' => 'add_group_members', 'classpath' => 'group/externallib.php', 'description' => 'Adds group members.', 'type' => 'write', 'capabilities' => 'moodle/course:managegroups', ), 'core_group_assign_grouping' => array( 'classname' => 'core_group_external', 'methodname' => 'assign_grouping', 'classpath' => 'group/externallib.php', 'description' => 'Assing groups from groupings', 'type' => 'write', ), 'core_group_create_groupings' => array( 'classname' => 'core_group_external', 'methodname' => 'create_groupings', 'classpath' => 'group/externallib.php', 'description' => 'Creates new groupings', 'type' => 'write', ), 'core_group_create_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'create_groups', 'classpath' => 'group/externallib.php', 'description' => 'Creates new groups.', 'type' => 'write', 'capabilities' => 'moodle/course:managegroups' ), 'core_group_delete_group_members' => array( 'classname' => 'core_group_external', 'methodname' => 'delete_group_members', 'classpath' => 'group/externallib.php', 'description' => 'Deletes group members.', 'type' => 'write', 'capabilities' => 'moodle/course:managegroups' ), 'core_group_delete_groupings' => array( 'classname' => 'core_group_external', 'methodname' => 'delete_groupings', 'classpath' => 'group/externallib.php', 'description' => 'Deletes all specified groupings.', 'type' => 'write', ), 'core_group_delete_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'delete_groups', 'classpath' => 'group/externallib.php', 'description' => 'Deletes all specified groups.', 'type' => 'write', 'capabilities' => 'moodle/course:managegroups' ), 'core_group_get_activity_allowed_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'get_activity_allowed_groups', 'classpath' => 'group/externallib.php', 'description' => 'Gets a list of groups that the user is allowed to access within the specified activity.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_group_get_activity_groupmode' => array( 'classname' => 'core_group_external', 'methodname' => 'get_activity_groupmode', 'classpath' => 'group/externallib.php', 'description' => 'Returns effective groupmode used in a given activity.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_group_get_course_groupings' => array( 'classname' => 'core_group_external', 'methodname' => 'get_course_groupings', 'classpath' => 'group/externallib.php', 'description' => 'Returns all groupings in specified course.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_group_get_course_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'get_course_groups', 'classpath' => 'group/externallib.php', 'description' => 'Returns all groups in specified course.', 'type' => 'read', 'ajax' => true, 'capabilities' => 'moodle/course:managegroups', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_group_get_course_user_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'get_course_user_groups', 'classpath' => 'group/externallib.php', 'description' => 'Returns all groups in specified course for the specified user.', 'type' => 'read', 'capabilities' => 'moodle/course:managegroups', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_group_get_group_members' => array( 'classname' => 'core_group_external', 'methodname' => 'get_group_members', 'classpath' => 'group/externallib.php', 'description' => 'Returns group members.', 'type' => 'read', 'capabilities' => 'moodle/course:managegroups' ), 'core_group_get_groupings' => array( 'classname' => 'core_group_external', 'methodname' => 'get_groupings', 'classpath' => 'group/externallib.php', 'description' => 'Returns groupings details.', 'type' => 'read', ), 'core_group_get_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'get_groups', 'classpath' => 'group/externallib.php', 'description' => 'Returns group details.', 'type' => 'read', 'capabilities' => 'moodle/course:managegroups' ), 'core_group_get_groups_for_selector' => [ 'classname' => 'core_group\external\get_groups_for_selector', 'description' => 'Get the group/(s) for a course', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_group_unassign_grouping' => array( 'classname' => 'core_group_external', 'methodname' => 'unassign_grouping', 'classpath' => 'group/externallib.php', 'description' => 'Unassing groups from groupings', 'type' => 'write', ), 'core_group_update_groupings' => array( 'classname' => 'core_group_external', 'methodname' => 'update_groupings', 'classpath' => 'group/externallib.php', 'description' => 'Updates existing groupings', 'type' => 'write', ), 'core_group_update_groups' => array( 'classname' => 'core_group_external', 'methodname' => 'update_groups', 'classpath' => 'group/externallib.php', 'description' => 'Updates existing groups.', 'type' => 'write', 'capabilities' => 'moodle/course:managegroups' ), 'core_message_mute_conversations' => array( 'classname' => 'core_message_external', 'methodname' => 'mute_conversations', 'classpath' => 'message/externallib.php', 'description' => 'Mutes a list of conversations', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_unmute_conversations' => array( 'classname' => 'core_message_external', 'methodname' => 'unmute_conversations', 'classpath' => 'message/externallib.php', 'description' => 'Unmutes a list of conversations', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_block_user' => array( 'classname' => 'core_message_external', 'methodname' => 'block_user', 'classpath' => 'message/externallib.php', 'description' => 'Blocks a user', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_contact_requests' => array( 'classname' => 'core_message_external', 'methodname' => 'get_contact_requests', 'classpath' => 'message/externallib.php', 'description' => 'Returns contact requests for a user', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_create_contact_request' => array( 'classname' => 'core_message_external', 'methodname' => 'create_contact_request', 'classpath' => 'message/externallib.php', 'description' => 'Creates a contact request', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_confirm_contact_request' => array( 'classname' => 'core_message_external', 'methodname' => 'confirm_contact_request', 'classpath' => 'message/externallib.php', 'description' => 'Confirms a contact request', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_decline_contact_request' => array( 'classname' => 'core_message_external', 'methodname' => 'decline_contact_request', 'classpath' => 'message/externallib.php', 'description' => 'Declines a contact request', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_received_contact_requests_count' => array( 'classname' => 'core_message_external', 'methodname' => 'get_received_contact_requests_count', 'classpath' => 'message/externallib.php', 'description' => 'Gets the number of received contact requests', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_delete_contacts' => array( 'classname' => 'core_message_external', 'methodname' => 'delete_contacts', 'classpath' => 'message/externallib.php', 'description' => 'Remove contacts from the contact list', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_delete_conversations_by_id' => array( 'classname' => 'core_message_external', 'methodname' => 'delete_conversations_by_id', 'classpath' => 'message/externallib.php', 'description' => 'Deletes a list of conversations.', 'type' => 'write', 'capabilities' => 'moodle/site:deleteownmessage', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_delete_message' => array( 'classname' => 'core_message_external', 'methodname' => 'delete_message', 'classpath' => 'message/externallib.php', 'description' => 'Deletes a message.', 'type' => 'write', 'capabilities' => 'moodle/site:deleteownmessage', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_blocked_users' => array( 'classname' => 'core_message_external', 'methodname' => 'get_blocked_users', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a list of users blocked', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_data_for_messagearea_search_messages' => array( 'classname' => 'core_message_external', 'methodname' => 'data_for_messagearea_search_messages', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve the template data for searching for messages', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_message_search_users' => array( 'classname' => 'core_message_external', 'methodname' => 'message_search_users', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve the data for searching for people', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_user_contacts' => array( 'classname' => 'core_message_external', 'methodname' => 'get_user_contacts', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve the contact list', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_conversations' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversations', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a list of conversations for a user', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true ), 'core_message_get_conversation' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversation', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a conversation for a user', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true ), 'core_message_get_conversation_between_users' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversation_between_users', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a conversation for a user between another user', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true ), 'core_message_get_self_conversation' => array( 'classname' => 'core_message_external', 'methodname' => 'get_self_conversation', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a self-conversation for a user', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true ), 'core_message_get_messages' => array( 'classname' => 'core_message_external', 'methodname' => 'get_messages', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a list of messages sent and received by a user (conversations, notifications or both)', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_get_conversation_counts' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversation_counts', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a list of conversation counts, indexed by type.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_get_unread_conversation_counts' => array( 'classname' => 'core_message_external', 'methodname' => 'get_unread_conversation_counts', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a list of unread conversation counts, indexed by type.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_get_conversation_members' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversation_members', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a list of members in a conversation', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_get_member_info' => array( 'classname' => 'core_message_external', 'methodname' => 'get_member_info', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve a user message profiles', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_get_unread_conversations_count' => array( 'classname' => 'core_message_external', 'methodname' => 'get_unread_conversations_count', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve the count of unread conversations for a given user', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'readonlysession' => true, // We don't modify the session. ), 'core_message_mark_all_notifications_as_read' => array( 'classname' => 'core_message_external', 'methodname' => 'mark_all_notifications_as_read', 'classpath' => 'message/externallib.php', 'description' => 'Mark all notifications as read for a given user', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_mark_all_conversation_messages_as_read' => array( 'classname' => 'core_message_external', 'methodname' => 'mark_all_conversation_messages_as_read', 'classpath' => 'message/externallib.php', 'description' => 'Mark all conversation messages as read for a given user', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_mark_message_read' => array( 'classname' => 'core_message_external', 'methodname' => 'mark_message_read', 'classpath' => 'message/externallib.php', 'description' => 'Mark a single message as read, trigger message_viewed event.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_mark_notification_read' => array( 'classname' => 'core_message_external', 'methodname' => 'mark_notification_read', 'classpath' => 'message/externallib.php', 'description' => 'Mark a single notification as read, trigger notification_viewed event.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true, ), 'core_message_message_processor_config_form' => array( 'classname' => 'core_message_external', 'methodname' => 'message_processor_config_form', 'classpath' => 'message/externallib.php', 'description' => 'Process the message processor config form', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_message_processor' => array( 'classname' => 'core_message_external', 'methodname' => 'get_message_processor', 'classpath' => 'message/externallib.php', 'description' => 'Get a message processor', 'type' => 'read', 'ajax' => true, ), 'core_message_search_contacts' => array( 'classname' => 'core_message_external', 'methodname' => 'search_contacts', 'classpath' => 'message/externallib.php', 'description' => 'Search for contacts', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_send_instant_messages' => array( 'classname' => 'core_message_external', 'methodname' => 'send_instant_messages', 'classpath' => 'message/externallib.php', 'description' => 'Send instant messages', 'type' => 'write', 'capabilities' => 'moodle/site:sendmessage', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'readonlysession' => true, ), 'core_message_send_messages_to_conversation' => array( 'classname' => 'core_message_external', 'methodname' => 'send_messages_to_conversation', 'classpath' => 'message/externallib.php', 'description' => 'Send messages to an existing conversation between users', 'type' => 'write', 'capabilities' => 'moodle/site:sendmessage', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_conversation_messages' => array( 'classname' => 'core_message_external', 'methodname' => 'get_conversation_messages', 'classpath' => 'message/externallib.php', 'description' => 'Retrieve the conversation messages and relevant member information', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_unblock_user' => array( 'classname' => 'core_message_external', 'methodname' => 'unblock_user', 'classpath' => 'message/externallib.php', 'description' => 'Unblocks a user', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_user_notification_preferences' => array( 'classname' => 'core_message_external', 'methodname' => 'get_user_notification_preferences', 'classpath' => 'message/externallib.php', 'description' => 'Get the notification preferences for a given user.', 'type' => 'read', 'capabilities' => 'moodle/user:editownmessageprofile', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_user_message_preferences' => array( 'classname' => 'core_message_external', 'methodname' => 'get_user_message_preferences', 'classpath' => 'message/externallib.php', 'description' => 'Get the message preferences for a given user.', 'type' => 'read', 'capabilities' => 'moodle/user:editownmessageprofile', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), 'ajax' => true ), 'core_message_set_favourite_conversations' => array( 'classname' => 'core_message_external', 'methodname' => 'set_favourite_conversations', 'classpath' => 'message/externallib.php', 'description' => 'Mark a conversation or group of conversations as favourites/starred conversations.', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_unset_favourite_conversations' => array( 'classname' => 'core_message_external', 'methodname' => 'unset_favourite_conversations', 'classpath' => 'message/externallib.php', 'description' => 'Unset a conversation or group of conversations as favourites/starred conversations.', 'type' => 'write', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_delete_message_for_all_users' => array( 'classname' => 'core_message_external', 'methodname' => 'delete_message_for_all_users', 'classpath' => 'message/externallib.php', 'description' => 'Deletes a message for all users.', 'type' => 'write', 'capabilities' => 'moodle/site:deleteanymessage', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_message_get_unread_notification_count' => [ 'classname' => '\core_message\external\get_unread_notification_count', 'description' => 'Get number of unread notifications.', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_message_set_default_notification' => [ 'classname' => 'core_message\external\message_set_default_notification', 'description' => 'Set the default value for a given notification preference', 'capabilities' => 'moodle/site:config', 'type' => 'write', 'ajax' => true, ], 'core_my_view_page' => [ 'classname' => '\core_my\external\view_page', 'methodname' => 'execute', 'description' => 'Trigger the My or Dashboard viewed event.', 'type' => 'write', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_notes_create_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'create_notes', 'classpath' => 'notes/externallib.php', 'description' => 'Create notes', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/notes:manage', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_notes_delete_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'delete_notes', 'classpath' => 'notes/externallib.php', 'description' => 'Delete notes', 'type' => 'write', 'capabilities' => 'moodle/notes:manage', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_notes_get_course_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'get_course_notes', 'classpath' => 'notes/externallib.php', 'description' => 'Returns all notes in specified course (or site), for the specified user.', 'type' => 'read', 'capabilities' => 'moodle/notes:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_notes_get_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'get_notes', 'classpath' => 'notes/externallib.php', 'description' => 'Get notes', 'type' => 'read', 'capabilities' => 'moodle/notes:view' ), 'core_notes_update_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'update_notes', 'classpath' => 'notes/externallib.php', 'description' => 'Update notes', 'type' => 'write', 'capabilities' => 'moodle/notes:manage' ), 'core_notes_view_notes' => array( 'classname' => 'core_notes_external', 'methodname' => 'view_notes', 'classpath' => 'notes/externallib.php', 'description' => 'Simulates the web interface view of notes/index.php: trigger events.', 'type' => 'write', 'capabilities' => 'moodle/notes:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_output_load_template' => array( 'classname' => 'core\output\external', 'methodname' => 'load_template', 'description' => 'Load a template for a renderable', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, ), 'core_output_load_template_with_dependencies' => array( 'classname' => 'core\output\external', 'methodname' => 'load_template_with_dependencies', 'description' => 'Load a template and its dependencies for a renderable', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, ), 'core_output_load_fontawesome_icon_system_map' => array( 'classname' => 'core\external\output\icon_system\load_fontawesome_map', 'description' => 'Load the mapping of moodle pix names to fontawesome icon names', 'type' => 'read', 'loginrequired' => false, 'ajax' => true, ), // Question related functions. 'core_question_update_flag' => array( 'classname' => 'core_question_external', 'methodname' => 'update_flag', 'description' => 'Update the flag state of a question attempt.', 'type' => 'write', 'capabilities' => 'moodle/question:flag', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_question_get_random_question_summaries' => array( 'classname' => 'core_question_external', 'methodname' => 'get_random_question_summaries', 'description' => 'Get the random question set for a criteria', 'type' => 'read', 'ajax' => true, ), 'core_rating_get_item_ratings' => array( 'classname' => 'core_rating_external', 'methodname' => 'get_item_ratings', 'description' => 'Retrieve all the ratings for an item.', 'type' => 'read', 'capabilities' => 'moodle/rating:view', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_rating_add_rating' => array( 'classname' => 'core_rating_external', 'methodname' => 'add_rating', 'description' => 'Rates an item.', 'type' => 'write', 'capabilities' => 'moodle/rating:rate', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_role_assign_roles' => array( 'classname' => 'core_role_external', 'methodname' => 'assign_roles', 'classpath' => 'enrol/externallib.php', 'description' => 'Manual role assignments.', 'type' => 'write', 'capabilities' => 'moodle/role:assign' ), 'core_role_unassign_roles' => array( 'classname' => 'core_role_external', 'methodname' => 'unassign_roles', 'classpath' => 'enrol/externallib.php', 'description' => 'Manual role unassignments.', 'type' => 'write', 'capabilities' => 'moodle/role:assign' ), 'core_search_get_relevant_users' => array( 'classname' => '\core_search\external', 'methodname' => 'get_relevant_users', 'description' => 'Gets relevant users for a search request.', 'type' => 'read', 'ajax' => true ), 'core_search_get_results' => [ 'classname' => '\core_search\external\get_results', 'description' => 'Get search results.', 'type' => 'read', 'capabilities' => 'moodle/search:query', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_search_get_search_areas_list' => [ 'classname' => '\core_search\external\get_search_areas_list', 'description' => 'Get search areas.', 'type' => 'read', 'capabilities' => 'moodle/search:query', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_search_view_results' => [ 'classname' => '\core_search\external\view_results', 'description' => 'Trigger view search results event.', 'type' => 'write', 'capabilities' => 'moodle/search:query', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_search_get_top_results' => [ 'classname' => '\core_search\external\get_top_results', 'description' => 'Get top search results.', 'type' => 'read', 'capabilities' => 'moodle/search:query', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_tag_get_tagindex' => array( 'classname' => 'core_tag_external', 'methodname' => 'get_tagindex', 'description' => 'Gets tag index page for one tag and one tag area', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_tag_get_tags' => array( 'classname' => 'core_tag_external', 'methodname' => 'get_tags', 'description' => 'Gets tags by their ids', 'type' => 'read', 'ajax' => true, ), 'core_tag_update_tags' => array( 'classname' => 'core_tag_external', 'methodname' => 'update_tags', 'description' => 'Updates tags', 'type' => 'write', 'ajax' => true, ), 'core_tag_get_tagindex_per_area' => array( 'classname' => 'core_tag_external', 'methodname' => 'get_tagindex_per_area', 'description' => 'Gets tag index page per different areas.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_tag_get_tag_areas' => array( 'classname' => 'core_tag_external', 'methodname' => 'get_tag_areas', 'description' => 'Retrieves existing tag areas.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_tag_get_tag_collections' => array( 'classname' => 'core_tag_external', 'methodname' => 'get_tag_collections', 'description' => 'Retrieves existing tag collections.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_tag_get_tag_cloud' => array( 'classname' => 'core_tag_external', 'methodname' => 'get_tag_cloud', 'description' => 'Retrieves a tag cloud for the given collection and/or query search.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_update_inplace_editable' => array( 'classname' => 'core_external', 'methodname' => 'update_inplace_editable', 'classpath' => 'lib/external/externallib.php', 'description' => 'Generic service to update title', 'type' => 'write', 'loginrequired' => true, 'ajax' => true, ), 'core_user_add_user_device' => array( 'classname' => 'core_user_external', 'methodname' => 'add_user_device', 'classpath' => 'user/externallib.php', 'description' => 'Store mobile user devices information for PUSH Notifications.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_update_user_device_public_key' => array( 'classname' => '\core_user\external\update_user_device_public_key', 'description' => 'Store mobile user public key.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_add_user_private_files' => array( 'classname' => 'core_user_external', 'methodname' => 'add_user_private_files', 'classpath' => 'user/externallib.php', 'description' => 'Copy files from a draft area to users private files area.', 'type' => 'write', 'capabilities' => 'moodle/user:manageownfiles', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_create_users' => array( 'classname' => 'core_user_external', 'methodname' => 'create_users', 'classpath' => 'user/externallib.php', 'description' => 'Create users.', 'type' => 'write', 'capabilities' => 'moodle/user:create' ), 'core_user_delete_users' => array( 'classname' => 'core_user_external', 'methodname' => 'delete_users', 'classpath' => 'user/externallib.php', 'description' => 'Delete users.', 'type' => 'write', 'capabilities' => 'moodle/user:delete' ), 'core_user_get_course_user_profiles' => array( 'classname' => 'core_user_external', 'methodname' => 'get_course_user_profiles', 'classpath' => 'user/externallib.php', 'description' => 'Get course user profiles (each of the profils matching a course id and a user id),.', 'type' => 'read', 'capabilities' => 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update, ' . 'moodle/site:accessallgroups', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_get_users' => array( 'classname' => 'core_user_external', 'methodname' => 'get_users', 'classpath' => 'user/externallib.php', 'description' => 'search for users matching the parameters', 'type' => 'read', 'capabilities' => 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update' ), 'core_user_get_users_by_field' => array( 'classname' => 'core_user_external', 'methodname' => 'get_users_by_field', 'classpath' => 'user/externallib.php', 'description' => 'Retrieve users\' information for a specified unique field - If you want to do a user search, use ' . 'core_user_get_users() or core_user_search_identity().', 'type' => 'read', 'capabilities' => 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_search_identity' => array( 'classname' => '\core_user\external\search_identity', 'description' => 'Return list of users identities matching the given criteria in their name or other identity fields.', 'type' => 'read', 'capabilities' => 'moodle/user:viewalldetails', 'ajax' => true, 'loginrequired' => true, ), 'core_user_remove_user_device' => array( 'classname' => 'core_user_external', 'methodname' => 'remove_user_device', 'classpath' => 'user/externallib.php', 'description' => 'Remove a user device from the Moodle database.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_update_users' => array( 'classname' => 'core_user_external', 'methodname' => 'update_users', 'classpath' => 'user/externallib.php', 'description' => 'Update users.', 'type' => 'write', 'capabilities' => 'moodle/user:update', 'ajax' => true, ), 'core_user_update_user_preferences' => array( 'classname' => 'core_user_external', 'methodname' => 'update_user_preferences', 'classpath' => 'user/externallib.php', 'description' => 'Update a user\'s preferences', 'type' => 'write', 'capabilities' => 'moodle/user:editownmessageprofile, moodle/user:editmessageprofile', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_view_user_list' => array( 'classname' => 'core_user_external', 'methodname' => 'view_user_list', 'classpath' => 'user/externallib.php', 'description' => 'Simulates the web-interface view of user/index.php (triggering events),.', 'type' => 'write', 'capabilities' => 'moodle/course:viewparticipants', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_view_user_profile' => array( 'classname' => 'core_user_external', 'methodname' => 'view_user_profile', 'classpath' => 'user/externallib.php', 'description' => 'Simulates the web-interface view of user/view.php and user/profile.php (triggering events),.', 'type' => 'write', 'capabilities' => 'moodle/user:viewdetails', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_get_user_preferences' => array( 'classname' => 'core_user_external', 'methodname' => 'get_user_preferences', 'classpath' => 'user/externallib.php', 'description' => 'Return user preferences.', 'type' => 'read', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_update_picture' => array( 'classname' => 'core_user_external', 'methodname' => 'update_picture', 'classpath' => 'user/externallib.php', 'description' => 'Update or delete the user picture in the site', 'type' => 'write', 'capabilities' => 'moodle/user:editownprofile, moodle/user:editprofile', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_set_user_preferences' => array( 'classname' => 'core_user_external', 'methodname' => 'set_user_preferences', 'classpath' => 'user/externallib.php', 'description' => 'Set user preferences.', 'type' => 'write', 'capabilities' => 'moodle/site:config', 'ajax' => true, 'loginrequired' => false, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_agree_site_policy' => array( 'classname' => 'core_user_external', 'methodname' => 'agree_site_policy', 'classpath' => 'user/externallib.php', 'description' => 'Agree the site policy for the current user.', 'type' => 'write', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_get_private_files_info' => array( 'classname' => 'core_user_external', 'methodname' => 'get_private_files_info', 'classpath' => 'user/externallib.php', 'description' => 'Returns general information about files in the user private files area.', 'type' => 'read', 'capabilities' => 'moodle/user:manageownfiles', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_user_prepare_private_files_for_edition' => [ 'classname' => '\core_user\external\prepare_private_files_for_edition', 'description' => 'Prepares the draft area for user private files.', 'type' => 'write', 'capabilities' => 'moodle/user:manageownfiles', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_user_update_private_files' => [ 'classname' => '\core_user\external\update_private_files', 'description' => 'Copy files from a draft area to users private files area.', 'type' => 'write', 'capabilities' => 'moodle/user:manageownfiles', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], // Competencies functions. 'core_competency_create_competency_framework' => array( 'classname' => 'core_competency\external', 'methodname' => 'create_competency_framework', 'classpath' => '', 'description' => 'Creates new competency frameworks.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_read_competency_framework' => array( 'classname' => 'core_competency\external', 'methodname' => 'read_competency_framework', 'classpath' => '', 'description' => 'Load a summary of a competency framework.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_duplicate_competency_framework' => array( 'classname' => 'core_competency\external', 'methodname' => 'duplicate_competency_framework', 'classpath' => '', 'description' => 'Duplicate a competency framework.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_delete_competency_framework' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_competency_framework', 'classpath' => '', 'description' => 'Delete a competency framework.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_update_competency_framework' => array( 'classname' => 'core_competency\external', 'methodname' => 'update_competency_framework', 'classpath' => '', 'description' => 'Update a competency framework.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_list_competency_frameworks' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_competency_frameworks', 'classpath' => '', 'description' => 'Load a list of a competency frameworks.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_count_competency_frameworks' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_competency_frameworks', 'classpath' => '', 'description' => 'Count a list of a competency frameworks.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_competency_framework_viewed' => array( 'classname' => 'core_competency\external', 'methodname' => 'competency_framework_viewed', 'classpath' => '', 'description' => 'Log event competency framework viewed', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_create_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'create_competency', 'classpath' => '', 'description' => 'Creates new competencies.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_read_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'read_competency', 'classpath' => '', 'description' => 'Load a summary of a competency.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_competency_viewed' => array( 'classname' => 'core_competency\external', 'methodname' => 'competency_viewed', 'classpath' => '', 'description' => 'Log event competency viewed', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_delete_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_competency', 'classpath' => '', 'description' => 'Delete a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_update_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'update_competency', 'classpath' => '', 'description' => 'Update a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_list_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_competencies', 'classpath' => '', 'description' => 'Load a list of a competencies.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_list_competencies_in_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_competencies_in_template', 'classpath' => '', 'description' => 'Load a list of a competencies for a given template.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_count_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_competencies', 'classpath' => '', 'description' => 'Count a list of a competencies.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_count_competencies_in_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_competencies_in_template', 'classpath' => '', 'description' => 'Count a list of a competencies for a given template.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_search_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'search_competencies', 'classpath' => '', 'description' => 'Search a list of a competencies.', 'type' => 'read', 'capabilities' => 'moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_set_parent_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'set_parent_competency', 'classpath' => '', 'description' => 'Set a new parent for a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_move_up_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'move_up_competency', 'classpath' => '', 'description' => 'Re-order a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_move_down_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'move_down_competency', 'classpath' => '', 'description' => 'Re-order a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_list_course_module_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_course_module_competencies', 'classpath' => '', 'description' => 'List the competencies in a course module', 'type' => 'read', 'capabilities' => 'moodle/competency:coursecompetencyview', 'ajax' => true, ), 'core_competency_count_course_module_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_course_module_competencies', 'classpath' => '', 'description' => 'Count the competencies in a course module', 'type' => 'read', 'capabilities' => 'moodle/competency:coursecompetencyview', 'ajax' => true, ), 'core_competency_list_course_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_course_competencies', 'classpath' => '', 'description' => 'List the competencies in a course', 'type' => 'read', 'capabilities' => 'moodle/competency:coursecompetencyview', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_count_competencies_in_course' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_competencies_in_course', 'classpath' => '', 'description' => 'List the competencies in a course', 'type' => 'read', 'capabilities' => 'moodle/competency:coursecompetencyview', 'ajax' => true, ), 'core_competency_count_courses_using_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_courses_using_competency', 'classpath' => '', 'description' => 'List the courses using a competency', 'type' => 'read', 'capabilities' => 'moodle/competency:coursecompetencyview', 'ajax' => true, ), 'core_competency_add_competency_to_course' => array( 'classname' => 'core_competency\external', 'methodname' => 'add_competency_to_course', 'classpath' => '', 'description' => 'Add the competency to a course', 'type' => 'write', 'capabilities' => 'moodle/competency:coursecompetencymanage', 'ajax' => true, ), 'core_competency_add_competency_to_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'add_competency_to_template', 'classpath' => '', 'description' => 'Add the competency to a template', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_remove_competency_from_course' => array( 'classname' => 'core_competency\external', 'methodname' => 'remove_competency_from_course', 'classpath' => '', 'description' => 'Remove a competency from a course', 'type' => 'write', 'capabilities' => 'moodle/competency:coursecompetencymanage', 'ajax' => true, ), 'core_competency_set_course_competency_ruleoutcome' => array( 'classname' => 'core_competency\external', 'methodname' => 'set_course_competency_ruleoutcome', 'classpath' => '', 'description' => 'Modify the ruleoutcome value for course competency', 'type' => 'write', 'capabilities' => 'moodle/competency:coursecompetencymanage', 'ajax' => true, ), 'core_competency_remove_competency_from_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'remove_competency_from_template', 'classpath' => '', 'description' => 'Remove a competency from a template', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_reorder_course_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'reorder_course_competency', 'classpath' => '', 'description' => 'Move a course competency to a new relative sort order.', 'type' => 'write', 'capabilities' => 'moodle/competency:coursecompetencymanage', 'ajax' => true, ), 'core_competency_reorder_template_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'reorder_template_competency', 'classpath' => '', 'description' => 'Move a template competency to a new relative sort order.', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_create_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'create_template', 'classpath' => '', 'description' => 'Creates new learning plan templates.', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_duplicate_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'duplicate_template', 'classpath' => '', 'description' => 'Duplicate learning plan template.', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_read_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'read_template', 'classpath' => '', 'description' => 'Load a summary of a learning plan template.', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_delete_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_template', 'classpath' => '', 'description' => 'Delete a learning plan template.', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_update_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'update_template', 'classpath' => '', 'description' => 'Update a learning plan template.', 'type' => 'write', 'capabilities' => 'moodle/competency:templatemanage', 'ajax' => true, ), 'core_competency_list_templates' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_templates', 'classpath' => '', 'description' => 'Load a list of a learning plan templates.', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_list_templates_using_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_templates_using_competency', 'classpath' => '', 'description' => 'Load a list of a learning plan templates for a given competency.', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_count_templates' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_templates', 'classpath' => '', 'description' => 'Count a list of a learning plan templates.', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_count_templates_using_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'count_templates_using_competency', 'classpath' => '', 'description' => 'Count a list of a learning plan templates for a given competency.', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_create_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'create_plan', 'classpath' => '', 'description' => 'Creates a learning plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_update_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'update_plan', 'classpath' => '', 'description' => 'Updates a learning plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_complete_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'complete_plan', 'classpath' => '', 'description' => 'Complete learning plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_reopen_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'reopen_plan', 'classpath' => '', 'description' => 'Reopen learning plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_read_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'read_plan', 'classpath' => '', 'description' => 'Load a learning plan.', 'type' => 'read', 'capabilities' => 'moodle/competency:planviewown', 'ajax' => true, ), 'core_competency_delete_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_plan', 'classpath' => '', 'description' => 'Delete a learning plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_list_user_plans' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_user_plans', 'classpath' => '', 'description' => 'List a user\'s learning plans.', 'type' => 'read', 'capabilities' => 'moodle/competency:planviewown', 'ajax' => true, ), 'core_competency_list_plan_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'list_plan_competencies', 'classpath' => '', 'description' => 'List the competencies in a plan', 'type' => 'read', 'capabilities' => 'moodle/competency:planviewown', 'ajax' => true, ), 'core_competency_add_competency_to_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'add_competency_to_plan', 'classpath' => '', 'description' => 'Add the competency to a learning plan', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_remove_competency_from_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'remove_competency_from_plan', 'classpath' => '', 'description' => 'Remove the competency from a learning plan', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_reorder_plan_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'reorder_plan_competency', 'classpath' => '', 'description' => 'Move a plan competency to a new relative sort order.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_plan_request_review' => array( 'classname' => 'core_competency\external', 'methodname' => 'plan_request_review', 'classpath' => '', 'description' => 'Request for a plan to be reviewed.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanagedraft', 'ajax' => true, ), 'core_competency_plan_start_review' => array( 'classname' => 'core_competency\external', 'methodname' => 'plan_start_review', 'classpath' => '', 'description' => 'Start the review of a plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_plan_stop_review' => array( 'classname' => 'core_competency\external', 'methodname' => 'plan_stop_review', 'classpath' => '', 'description' => 'Stop the review of a plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_plan_cancel_review_request' => array( 'classname' => 'core_competency\external', 'methodname' => 'plan_cancel_review_request', 'classpath' => '', 'description' => 'Cancel the review of a plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanagedraft', 'ajax' => true, ), 'core_competency_approve_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'approve_plan', 'classpath' => '', 'description' => 'Approve a plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_unapprove_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'unapprove_plan', 'classpath' => '', 'description' => 'Unapprove a plan.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_template_has_related_data' => array( 'classname' => 'core_competency\external', 'methodname' => 'template_has_related_data', 'classpath' => '', 'description' => 'Check if a template has related data', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_get_scale_values' => array( 'classname' => 'core_competency\external', 'methodname' => 'get_scale_values', 'classpath' => '', 'description' => 'Fetch the values for a specific scale', 'type' => 'read', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_add_related_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'add_related_competency', 'classpath' => '', 'description' => 'Adds a related competency', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_remove_related_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'remove_related_competency', 'classpath' => '', 'description' => 'Remove a related competency', 'type' => 'write', 'capabilities' => 'moodle/competency:competencymanage', 'ajax' => true, ), 'core_competency_read_user_evidence' => array( 'classname' => 'core_competency\external', 'methodname' => 'read_user_evidence', 'classpath' => '', 'description' => 'Read an evidence of prior learning.', 'type' => 'read', 'capabilities' => 'moodle/competency:userevidenceview', 'ajax' => true, ), 'core_competency_delete_user_evidence' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_user_evidence', 'classpath' => '', 'description' => 'Delete an evidence of prior learning.', 'type' => 'write', 'capabilities' => 'moodle/competency:userevidencemanageown', 'ajax' => true, ), 'core_competency_create_user_evidence_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'create_user_evidence_competency', 'classpath' => '', 'description' => 'Create an evidence of prior learning relationship with a competency.', 'type' => 'read', 'capabilities' => 'moodle/competency:userevidencemanageown, moodle/competency:competencyview', 'ajax' => true, ), 'core_competency_delete_user_evidence_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_user_evidence_competency', 'classpath' => '', 'description' => 'Delete an evidence of prior learning relationship with a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:userevidencemanageown', 'ajax' => true, ), 'core_competency_user_competency_cancel_review_request' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_cancel_review_request', 'classpath' => '', 'description' => 'Cancel a review request.', 'type' => 'write', 'capabilities' => 'moodle/competency:userevidencemanageown', 'ajax' => true, ), 'core_competency_user_competency_request_review' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_request_review', 'classpath' => '', 'description' => 'Request a review.', 'type' => 'write', 'capabilities' => 'moodle/competency:userevidencemanageown', 'ajax' => true, ), 'core_competency_user_competency_start_review' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_start_review', 'classpath' => '', 'description' => 'Start a review.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencygrade', 'ajax' => true, ), 'core_competency_user_competency_stop_review' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_stop_review', 'classpath' => '', 'description' => 'Stop a review.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencygrade', 'ajax' => true, ), 'core_competency_user_competency_viewed' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_viewed', 'classpath' => '', 'description' => 'Log the user competency viewed event.', 'type' => 'read', 'capabilities' => 'moodle/competency:usercompetencyview', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_user_competency_viewed_in_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_viewed_in_plan', 'classpath' => '', 'description' => 'Log the user competency viewed in plan event.', 'type' => 'read', 'capabilities' => 'moodle/competency:usercompetencyview', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_user_competency_viewed_in_course' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_viewed_in_course', 'classpath' => '', 'description' => 'Log the user competency viewed in course event', 'type' => 'read', 'capabilities' => 'moodle/competency:usercompetencyview', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_user_competency_plan_viewed' => array( 'classname' => 'core_competency\external', 'methodname' => 'user_competency_plan_viewed', 'classpath' => '', 'description' => 'Log the user competency plan viewed event.', 'type' => 'read', 'capabilities' => 'moodle/competency:usercompetencyview', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_grade_competency' => array( 'classname' => 'core_competency\external', 'methodname' => 'grade_competency', 'classpath' => '', 'description' => 'Grade a competency.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencygrade', 'ajax' => true, ), 'core_competency_grade_competency_in_plan' => array( 'classname' => 'core_competency\external', 'methodname' => 'grade_competency_in_plan', 'classpath' => '', 'description' => 'Grade a competency from the user plan page.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencygrade', 'ajax' => true, ), 'core_competency_grade_competency_in_course' => array( 'classname' => 'core_competency\external', 'methodname' => 'grade_competency_in_course', 'classpath' => '', 'description' => 'Grade a competency from the course page.', 'type' => 'write', 'capabilities' => 'moodle/competency:competencygrade', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_competency_unlink_plan_from_template' => array( 'classname' => 'core_competency\external', 'methodname' => 'unlink_plan_from_template', 'classpath' => '', 'description' => 'Unlink a plan form it template.', 'type' => 'write', 'capabilities' => 'moodle/competency:planmanage', 'ajax' => true, ), 'core_competency_template_viewed' => array( 'classname' => 'core_competency\external', 'methodname' => 'template_viewed', 'classpath' => '', 'description' => 'Log event template viewed', 'type' => 'read', 'capabilities' => 'moodle/competency:templateview', 'ajax' => true, ), 'core_competency_request_review_of_user_evidence_linked_competencies' => array( 'classname' => 'core_competency\external', 'methodname' => 'request_review_of_user_evidence_linked_competencies', 'classpath' => '', 'description' => 'Send user evidence competencies in review', 'type' => 'write', 'capabilities' => 'moodle/competency:userevidencemanageown', 'ajax' => true, ), 'core_competency_update_course_competency_settings' => array( 'classname' => 'core_competency\external', 'methodname' => 'update_course_competency_settings', 'classpath' => '', 'description' => 'Update the course competency settings', 'type' => 'write', 'capabilities' => 'moodle/competency:coursecompetencyconfigure', 'ajax' => true, ), 'core_competency_delete_evidence' => array( 'classname' => 'core_competency\external', 'methodname' => 'delete_evidence', 'classpath' => '', 'description' => 'Delete an evidence', 'type' => 'write', 'capabilities' => 'moodle/competency:evidencedelete', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_webservice_get_site_info' => array( 'classname' => 'core_webservice_external', 'methodname' => 'get_site_info', 'classpath' => 'webservice/externallib.php', 'description' => 'Return some site info / user info / list web service functions', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), // Blocks functions. 'core_block_get_course_blocks' => array( 'classname' => 'core_block_external', 'methodname' => 'get_course_blocks', 'description' => 'Returns blocks information for a course.', 'type' => 'read', 'capabilities' => '', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_block_get_dashboard_blocks' => array( 'classname' => 'core_block_external', 'methodname' => 'get_dashboard_blocks', 'description' => 'Returns blocks information for the given user dashboard.', 'type' => 'read', 'capabilities' => '', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_block_fetch_addable_blocks' => array( 'classname' => 'core_block\external\fetch_addable_blocks', 'description' => 'Returns all addable blocks in a given page.', 'type' => 'read', 'capabilities' => 'moodle/site:manageblocks', 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), // Filters functions. 'core_filters_get_available_in_context' => array( 'classname' => 'core_filters\external\get_available_in_context', 'description' => 'Returns the filters available in the given contexts.', 'type' => 'read', 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), 'core_filters_get_all_states' => [ 'classname' => 'core_filters\external\get_all_states', 'description' => 'Retrieve all the filters and their states (including overridden ones in any context).', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_customfield_delete_field' => array( 'classname' => 'core_customfield_external', 'methodname' => 'delete_field', 'classpath' => 'customfield/externallib.php', 'description' => 'Deletes an entry', 'type' => 'write', 'ajax' => true, ), 'core_customfield_reload_template' => array( 'classname' => 'core_customfield_external', 'methodname' => 'reload_template', 'classpath' => 'customfield/externallib.php', 'description' => 'Reloads template', 'type' => 'read', 'ajax' => true, ), 'core_customfield_create_category' => array( 'classname' => 'core_customfield_external', 'methodname' => 'create_category', 'classpath' => 'customfield/externallib.php', 'description' => 'Creates a new category', 'type' => 'write', 'ajax' => true, ), 'core_customfield_delete_category' => array( 'classname' => 'core_customfield_external', 'methodname' => 'delete_category', 'classpath' => 'customfield/externallib.php', 'description' => 'Deletes a category', 'type' => 'write', 'ajax' => true, ), 'core_customfield_move_field' => array( 'classname' => 'core_customfield_external', 'methodname' => 'move_field', 'classpath' => 'customfield/externallib.php', 'description' => 'Drag and drop', 'type' => 'write', 'ajax' => true, ), 'core_customfield_move_category' => array( 'classname' => 'core_customfield_external', 'methodname' => 'move_category', 'classpath' => 'customfield/externallib.php', 'description' => 'Drag and drop categories', 'type' => 'write', 'ajax' => true, ), 'core_h5p_get_trusted_h5p_file' => [ 'classname' => 'core_h5p\external', 'methodname' => 'get_trusted_h5p_file', 'classpath' => '', 'description' => 'Get the H5P file cleaned for Mobile App.', 'type' => 'read', 'ajax' => true, 'capabilities' => '', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_table_get_dynamic_table_content' => [ 'classname' => 'core_table\external\dynamic\get', 'description' => 'Get the dynamic table content raw html', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_xapi_statement_post' => [ 'classname' => 'core_xapi\external\post_statement', 'classpath' => '', 'description' => 'Post an xAPI statement.', 'type' => 'write', 'ajax' => true, 'capabilities' => '', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_xapi_post_state' => [ 'classname' => 'core_xapi\external\post_state', 'classpath' => '', 'description' => 'Post an xAPI state into an activityId.', 'type' => 'write', 'ajax' => true, 'capabilities' => '', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_xapi_get_state' => [ 'classname' => 'core_xapi\external\get_state', 'classpath' => '', 'description' => 'Get an xAPI state data from an activityId.', 'type' => 'read', 'ajax' => true, 'capabilities' => '', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_xapi_get_states' => [ 'classname' => 'core_xapi\external\get_states', 'description' => 'Get all state ID from an activityId.', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_xapi_delete_state' => [ 'classname' => 'core_xapi\external\delete_state', 'classpath' => '', 'description' => 'Delete an xAPI state data from an activityId.', 'type' => 'write', 'ajax' => true, 'capabilities' => '', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_xapi_delete_states' => [ 'classname' => 'core_xapi\external\delete_states', 'description' => 'Delete all xAPI state data from an activityId.', 'type' => 'write', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_contentbank_delete_content' => [ 'classname' => 'core_contentbank\external\delete_content', 'classpath' => '', 'description' => 'Delete a content from the content bank.', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/contentbank:deleteanycontent', ], 'core_contentbank_rename_content' => [ 'classname' => 'core_contentbank\external\rename_content', 'classpath' => '', 'description' => 'Rename a content in the content bank.', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/contentbank:manageowncontent', ], 'core_contentbank_copy_content' => [ 'classname' => 'core_contentbank\external\copy_content', 'classpath' => '', 'description' => 'Copy a content in the content bank.', 'type' => 'write', 'ajax' => 'true', 'capabilities' => 'moodle/contentbank:copycontent, moodle/contentbank:copyanycontent', ], 'core_contentbank_set_content_visibility' => [ 'classname' => 'core_contentbank\external\set_content_visibility', 'classpath' => '', 'description' => 'Set the visibility of a content in the content bank.', 'type' => 'write', 'ajax' => true, 'capabilities' => 'moodle/contentbank:manageowncontent', ], 'core_create_userfeedback_action_record' => [ 'classname' => 'core\external\record_userfeedback_action', 'classpath' => '', 'description' => 'Record the action that the user takes in the user feedback notification for future use.', 'type' => 'write', 'ajax' => true, 'capabilities' => '', ], 'core_payment_get_available_gateways' => [ 'classname' => 'core_payment\external\get_available_gateways', 'description' => 'Get the list of payment gateways that support the given component/area', 'type' => 'read', 'ajax' => true, ], 'core_reportbuilder_filters_reset' => [ 'classname' => 'core_reportbuilder\external\filters\reset', 'description' => 'Reset filters for given report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_set_filters' => [ 'classname' => 'core_reportbuilder\external\filters\set', 'description' => 'Set filter values for given report', 'type' => 'write', 'ajax' => true, ], 'core_dynamic_tabs_get_content' => [ 'classname' => 'core\external\dynamic_tabs_get_content', 'description' => 'Returns the content for a dynamic tab', 'type' => 'read', 'ajax' => true, ], 'core_change_editmode' => [ 'classname' => 'core\external\editmode', 'methodname' => 'change_editmode', 'description' => 'Change the editing mode', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_reports_delete' => [ 'classname' => 'core_reportbuilder\external\reports\delete', 'description' => 'Delete report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_reports_get' => [ 'classname' => 'core_reportbuilder\external\reports\get', 'description' => 'Get custom report', 'type' => 'read', 'ajax' => true, ], 'core_reportbuilder_list_reports' => [ 'classname' => 'core_reportbuilder\external\reports\listing', 'description' => 'List custom reports for current user', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_reportbuilder_retrieve_report' => [ 'classname' => 'core_reportbuilder\external\reports\retrieve', 'description' => 'Retrieve custom report content', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_reportbuilder_retrieve_system_report' => [ 'classname' => 'core_reportbuilder\external\systemreports\retrieve', 'description' => 'Retrieve system report content', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_reportbuilder_can_view_system_report' => [ 'classname' => 'core_reportbuilder\external\systemreports\can_view', 'description' => 'Determine access to a system report', 'type' => 'read', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_reportbuilder_view_report' => [ 'classname' => 'core_reportbuilder\external\reports\view', 'description' => 'Trigger custom report viewed', 'type' => 'write', 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_reportbuilder_columns_add' => [ 'classname' => 'core_reportbuilder\external\columns\add', 'description' => 'Add column to report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_columns_delete' => [ 'classname' => 'core_reportbuilder\external\columns\delete', 'description' => 'Delete column from report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_columns_reorder' => [ 'classname' => 'core_reportbuilder\external\columns\reorder', 'description' => 'Re-order column within report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_columns_sort_get' => [ 'classname' => 'core_reportbuilder\external\columns\sort\get', 'description' => 'Retrieve column sorting for report', 'type' => 'read', 'ajax' => true, ], 'core_reportbuilder_columns_sort_reorder' => [ 'classname' => 'core_reportbuilder\external\columns\sort\reorder', 'description' => 'Re-order column sorting within report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_columns_sort_toggle' => [ 'classname' => 'core_reportbuilder\external\columns\sort\toggle', 'description' => 'Toggle sorting of column within report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_conditions_add' => [ 'classname' => 'core_reportbuilder\external\conditions\add', 'description' => 'Add condition to report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_conditions_delete' => [ 'classname' => 'core_reportbuilder\external\conditions\delete', 'description' => 'Delete condition from report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_conditions_reorder' => [ 'classname' => 'core_reportbuilder\external\conditions\reorder', 'description' => 'Re-order condition within report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_conditions_reset' => [ 'classname' => 'core_reportbuilder\external\conditions\reset', 'description' => 'Reset conditions for given report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_filters_add' => [ 'classname' => 'core_reportbuilder\external\filters\add', 'description' => 'Add filter to report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_filters_delete' => [ 'classname' => 'core_reportbuilder\external\filters\delete', 'description' => 'Delete filter from report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_filters_reorder' => [ 'classname' => 'core_reportbuilder\external\filters\reorder', 'description' => 'Re-order filter within report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_audiences_delete' => [ 'classname' => 'core_reportbuilder\external\audiences\delete', 'description' => 'Delete audience from report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_schedules_delete' => [ 'classname' => 'core_reportbuilder\external\schedules\delete', 'description' => 'Delete schedule from report', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_schedules_send' => [ 'classname' => 'core_reportbuilder\external\schedules\send', 'description' => 'Send report schedule', 'type' => 'write', 'ajax' => true, ], 'core_reportbuilder_schedules_toggle' => [ 'classname' => 'core_reportbuilder\external\schedules\toggle', 'description' => 'Toggle state of report schedule', 'type' => 'write', 'ajax' => true, ], 'core_admin_set_plugin_state' => [ 'classname' => 'core_admin\external\set_plugin_state', 'description' => 'Set the state of a plugin', 'type' => 'write', 'ajax' => true, ], 'core_admin_set_plugin_order' => [ 'classname' => 'core_admin\external\set_plugin_order', 'description' => 'Set the order of a plugin', 'type' => 'write', 'ajax' => true, ], 'core_admin_set_block_protection' => [ 'classname' => 'core_admin\external\set_block_protection', 'description' => 'Set the protection state for a block plugin', 'type' => 'write', 'ajax' => true, ], 'core_moodlenet_send_activity' => [ 'classname' => 'core\external\moodlenet_send_activity', 'description' => 'Send activity to MoodleNet', 'type' => 'read', 'ajax' => true, ], 'core_moodlenet_get_share_info_activity' => [ 'classname' => 'core\external\moodlenet_get_share_info_activity', 'description' => 'Get information about an activity being shared', 'type' => 'read', 'ajax' => true, ], 'core_moodlenet_auth_check' => [ 'classname' => 'core\external\moodlenet_auth_check', 'description' => 'Check a user has authorized for a given MoodleNet site', 'type' => 'write', 'ajax' => true, ], 'core_moodlenet_get_shared_course_info' => [ 'classname' => 'core\external\moodlenet_get_shared_course_info', 'description' => 'Get information about an course being shared', 'type' => 'read', 'ajax' => true, ], 'core_moodlenet_send_course' => [ 'classname' => 'core\external\moodlenet_send_course', 'description' => 'Send course to MoodleNet', 'type' => 'read', 'ajax' => true, ], 'core_output_poll_stored_progress' => [ 'classname' => 'core\external\output\poll_stored_progress', 'methodname' => 'execute', 'description' => 'Polls for the current percentage progress of a stored progress object', 'type' => 'read', 'ajax' => true, 'readonlysession' => true, ], 'core_ai_delete_provider_instance' => [ 'classname' => \core_ai\external\delete_provider_instance::class, 'description' => 'Delete a provider instance', 'type' => 'write', 'ajax' => true, ], 'core_ai_set_policy_status' => [ 'classname' => 'core_ai\external\set_policy_status', 'description' => 'Set a users AI policy acceptance', 'type' => 'write', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_ai_get_policy_status' => [ 'classname' => \core_ai\external\get_policy_status::class, 'description' => 'Get a users AI policy acceptance', 'type' => 'read', 'ajax' => true, 'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE], ], 'core_ai_set_action' => [ 'classname' => 'core_ai\external\set_action', 'description' => 'Update action', 'type' => 'write', 'ajax' => true, ], 'core_ai_set_provider_status' => [ 'classname' => \core_ai\external\set_provider_status::class, 'description' => 'Set a providers status', 'type' => 'write', 'ajax' => true, ], 'core_ai_set_provider_order' => [ 'classname' => \core_ai\external\set_provider_order::class, 'description' => 'Set the order of a provider', 'type' => 'write', 'ajax' => true, ], 'core_sms_set_gateway_status' => [ 'classname' => 'core_sms\external\sms_gateway_status', 'description' => 'Set the sms gateway status', 'type' => 'write', 'ajax' => true, ], 'core_question_move_questions' => [ 'classname' => '\core_question\external\move_questions', 'description' => 'Bulk move questions to a new category.', 'type' => 'write', 'ajax' => true, ], 'core_question_search_shared_banks' => [ 'classname' => '\core_question\external\search_shared_banks', 'description' => 'Get a list of shared question banks filtered by a search term.', 'type' => 'read', 'ajax' => true, ], 'core_message_set_unsent_message' => [ 'classname' => 'core_message\external\set_unsent_message', 'description' => 'Store an unsent message string', 'type' => 'write', 'ajax' => true, ], 'core_message_get_unsent_message' => [ 'classname' => 'core_message\external\get_unsent_message', 'description' => 'Get an unsent message string', 'type' => 'read', 'ajax' => true, ], ); $services = array( 'Moodle mobile web service' => array( 'functions' => array(), // Unused as we add the service in each function definition, third party services would use this. 'enabled' => 0, 'restrictedusers' => 0, 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, 'downloadfiles' => 1, 'uploadfiles' => 1 ), ); access.php000064400000234013152266401200006517 0ustar00. /** * Capability definitions for Moodle core. * * The capabilities are loaded into the database table when the module is * installed or updated. Whenever the capability definitions are updated, * the module version number should be bumped up. * * The system has four possible values for a capability: * CAP_ALLOW, CAP_PREVENT, CAP_PROHIBIT, and inherit (not set). * * * CAPABILITY NAMING CONVENTION * * It is important that capability names are unique. The naming convention * for capabilities that are specific to modules and blocks is as follows: * [mod/block]/: * * component_name should be the same as the directory name of the mod or block. * * Core moodle capabilities are defined thus: * moodle/: * * Examples: mod/forum:viewpost * block/recent_activity:view * moodle/site:deleteuser * * The variable name for the capability definitions array is $capabilities * * For more information, take a look to the documentation available: * - Access API: {@link https://moodledev.io/docs/apis/subsystems/access} * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @package core_access * @category access * @copyright 2006 onwards Martin Dougiamas http://dougiamas.com * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $capabilities = array( 'moodle/site:config' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG | RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( ) ), 'moodle/site:configview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, ) ), 'moodle/site:readallmessages' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW ) ), 'moodle/site:manageallmessaging' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/site:deleteanymessage' => array( 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/site:sendmessage' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW, 'user' => CAP_ALLOW ) ), 'moodle/site:senderrormessage' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ], 'moodle/site:deleteownmessage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), 'moodle/site:approvecourse' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/backup:backupcourse' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/site:backup' ), 'moodle/backup:backupsection' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/backup:backupcourse' ), 'moodle/backup:backupactivity' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/backup:backupcourse' ), 'moodle/backup:backuptargetimport' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/backup:backupcourse' ), 'moodle/backup:downloadfile' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/site:backupdownload' ), 'moodle/backup:configure' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/backup:userinfo' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/backup:anonymise' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/restore:restorecourse' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/site:restore' ), 'moodle/restore:restoresection' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/restore:restorecourse' ), 'moodle/restore:restoreactivity' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/restore:restorecourse' ), 'moodle/restore:viewautomatedfilearea' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), ), 'moodle/restore:restoretargetimport' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/site:import' ), 'moodle/restore:uploadfile' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/site:backupupload' ), 'moodle/restore:configure' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/restore:rolldates' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'coursecreator' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/restore:userinfo' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/restore:createuser' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/site:manageblocks' => array( 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_BLOCK, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/site:accessallgroups' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/site:viewanonymousevents' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'manager' => CAP_ALLOW, ) ), 'moodle/site:viewfullnames' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // In reports that give lists of users, extra information about each user's // identity (the fields configured in site option showuseridentity) will be // displayed to users who have this capability. 'moodle/site:viewuseridentity' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/site:viewreports' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/site:trustcontent' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/site:uploadusers' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // Permission to manage filter setting overrides in subcontexts. 'moodle/filter:manage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ) ), 'moodle/user:create' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/user:delete' => array( 'riskbitmask' => RISK_PERSONAL | RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/user:update' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/user:viewdetails' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'guest' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/user:viewprofilepictures' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, ], ], 'moodle/user:viewalldetails' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/user:update' ), 'moodle/user:viewlastip' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/user:update' ), 'moodle/user:viewhiddendetails' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/user:loginas' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS | RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // can the user manage the system default profile page? 'moodle/user:managesyspages' => array( 'riskbitmap' => RISK_SPAM | RISK_PERSONAL | RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // can the user manage another user's profile page? 'moodle/user:manageblocks' => array( 'riskbitmap' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_USER ), // can the user manage their own profile page? 'moodle/user:manageownblocks' => array( 'riskbitmap' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), // can the user manage their own files? 'moodle/user:manageownfiles' => array( 'riskbitmap' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), // Can the user ignore the setting userquota? // The permissions are cloned from ignorefilesizelimits as it was partly used for that purpose. 'moodle/user:ignoreuserquota' => array( 'riskbitmap' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'clonepermissionsfrom' => 'moodle/course:ignorefilesizelimits' ), // can the user manage the system default dashboard page? 'moodle/my:configsyspages' => array( 'riskbitmap' => RISK_SPAM | RISK_PERSONAL | RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/role:assign' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/role:review' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // The ability to override the permissions for any capability. 'moodle/role:override' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // The ability to override the permissions for 'safe' capabilities (those without risks). // If a user has moodle/role:override then you should not check this capability. 'moodle/role:safeoverride' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW ) ), 'moodle/role:manage' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/role:switchroles' => array( 'riskbitmask' => RISK_XSS | RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // Create, update and delete course categories. (Deleting a course category // does not let you delete the courses it contains, unless you also have // moodle/course: delete.) Creating and deleting requires this permission in // the parent category. 'moodle/category:manage' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/category:update' ), 'moodle/category:viewcourselist' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, ) ), 'moodle/category:viewhiddencategories' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'coursecreator' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/category:visibility' ), // create, delete, move cohorts in system and course categories, // (cohorts with component !== null can be only moved) 'moodle/cohort:manage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // add and remove cohort members (only for cohorts where component !== null) 'moodle/cohort:assign' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // View visible and hidden cohorts defined in the current context. 'moodle/cohort:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/cohort:configurecustomfields' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'clonepermissionsfrom' => 'moodle/site:config' ), 'moodle/group:configurecustomfields' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'clonepermissionsfrom' => 'moodle/site:config' ), 'moodle/course:create' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'coursecreator' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:creategroupconversations' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:request' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, ), 'moodle/course:delete' => array( 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/course:update' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, ) ), /* review course enrolments - no group restrictions, it is really full access to all participants info*/ 'moodle/course:enrolreview' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ) ), /* add, remove, hide enrol instances in courses */ 'moodle/course:enrolconfig' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ) ), 'moodle/course:reviewotherusers' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ), 'clonepermissionsfrom' => 'moodle/role:assign' ), 'moodle/course:bulkmessaging' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewhiddenuserfields' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewhiddencourses' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'coursecreator' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:visibility' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:managefiles' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:ignoreavailabilityrestrictions' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'teacher' => CAP_ALLOW, ), 'clonepermissionsfrom' => 'moodle/course:viewhiddenactivities' ), 'moodle/course:ignorefilesizelimits' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( ) ), 'moodle/course:manageactivities' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:activityvisibility' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewhiddenactivities' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewparticipants' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewoverview' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ], ], 'moodle/course:changefullname' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/course:changeshortname' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/course:changelockedcustomfields' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/course:configurecustomfields' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'clonepermissionsfrom' => 'moodle/site:config' ), 'moodle/course:renameroles' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/course:changeidnumber' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/course:changecategory' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/course:changesummary' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), // Ability to set a forced language for a course or activity. 'moodle/course:setforcedlanguage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/site:viewparticipants' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/course:isincompletionreports' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW, ), ), 'moodle/course:viewscales' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:managescales' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:managegroups' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewhiddengroups' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'READ', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:reset' => array( 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewsuspendedusers' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:tag' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/blog:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/blog:search' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/blog:viewdrafts' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/blog:create' => array( // works in CONTEXT_SYSTEM only 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/blog:manageentries' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/blog:manageexternal' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'student' => CAP_ALLOW, 'user' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/calendar:manageownentries' => array( // works in CONTEXT_SYSTEM only 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/calendar:managegroupentries' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/calendar:manageentries' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/user:editprofile' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/user:editownprofile' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'guest' => CAP_PROHIBIT, 'user' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/user:changeownpassword' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'guest' => CAP_PROHIBIT, 'user' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // The next 3 might make no sense for some roles, e.g teacher, etc. // since the next level up is site. These are more for the parent role 'moodle/user:readuserposts' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/user:readuserblogs' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // designed for parent role - not used in legacy roles 'moodle/user:viewuseractivitiesreport' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( ) ), //capabilities designed for the new message system configuration 'moodle/user:editmessageprofile' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/user:editownmessageprofile' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'guest' => CAP_PROHIBIT, 'user' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/question:managecategory' => array( 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), //new in moodle 1.9 'moodle/question:add' => array( 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:editmine' => array( 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:editall' => array( 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:viewmine' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:viewall' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:usemine' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:useall' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:movemine' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), 'moodle/question:moveall' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:manage' ), //END new in moodle 1.9 // Configure the installed question types. 'moodle/question:config' => array( 'riskbitmask' => RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), // While attempting questions, the ability to flag particular questions for later reference. 'moodle/question:flag' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // Controls whether the user can tag his own questions. 'moodle/question:tagmine' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:editmine' ), // Controls whether the user can tag all questions. 'moodle/question:tagall' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/question:editall' ), 'moodle/site:doclinks' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:sectionvisibility' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:useremail' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:viewhiddensections' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:setcurrentsection' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:movesections' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:update' ), 'moodle/site:mnetlogintoremote' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( ) ), 'moodle/grade:viewall' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, // and CONTEXT_USER 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:viewcoursegrades' ), 'moodle/grade:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW ) ), 'moodle/grade:viewhidden' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:viewcoursegrades' ), 'moodle/grade:import' => array( 'riskbitmask' => RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), 'moodle/grade:export' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), 'moodle/grade:manage' => array( 'riskbitmask' => RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), 'moodle/grade:edit' => array( 'riskbitmask' => RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), // ability to define advanced grading forms in activities either from scratch // or from a shared template 'moodle/grade:managegradingforms' => array( 'riskbitmask' => RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), // ability to save a grading form as a new shared template and eventually edit // and remove own templates (templates originally shared by that user) 'moodle/grade:sharegradingforms' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), // ability to edit and remove any shared template, even those originally shared // by other users 'moodle/grade:managesharedforms' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/grade:manageoutcomes' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), 'moodle/grade:manageletters' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:managegrades' ), 'moodle/grade:hide' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/grade:lock' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/grade:unlock' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/my:manageblocks' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), 'moodle/notes:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/notes:manage' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/tag:manage' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/tag:edit' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/tag:flag' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), 'moodle/tag:editblocks' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/block:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_BLOCK, 'archetypes' => array( 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), 'moodle/block:edit' => array( 'riskbitmask' => RISK_SPAM | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_BLOCK, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/portfolio:export' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), 'moodle/comment:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'frontpage' => CAP_ALLOW, 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/comment:post' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/comment:delete' => array( 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/webservice:createtoken' => array( 'riskbitmask' => RISK_CONFIG | RISK_DATALOSS | RISK_SPAM | RISK_PERSONAL | RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/webservice:managealltokens' => array( 'riskbitmask' => RISK_CONFIG | RISK_DATALOSS | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array() ), 'moodle/webservice:createmobiletoken' => array( 'riskbitmask' => RISK_SPAM | RISK_PERSONAL, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), 'moodle/rating:view' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/rating:viewany' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/rating:viewall' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/rating:rate' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:markcomplete' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/course:overridecompletion' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // Badges. 'moodle/badges:manageglobalsettings' => array( 'riskbitmask' => RISK_DATALOSS | RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW, ) ), // View available badges without earning them. 'moodle/badges:viewbadges' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, ) ), // Manage badges on own private badges page. 'moodle/badges:manageownbadges' => array( 'riskbitmap' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ) ), // View public badges in other users' profiles. 'moodle/badges:viewotherbadges' => array( 'riskbitmap' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ) ), // Earn badge. 'moodle/badges:earnbadge' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW, ) ), // Create/duplicate badges. 'moodle/badges:createbadge' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Delete badges. 'moodle/badges:deletebadge' => array( 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Set up/edit badge details. 'moodle/badges:configuredetails' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Set up/edit criteria of earning a badge. 'moodle/badges:configurecriteria' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Configure badge messages. 'moodle/badges:configuremessages' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Award badge to a user. 'moodle/badges:awardbadge' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Revoke badge from a user. 'moodle/badges:revokebadge' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // View users who earned a specific badge without being able to award a badge. 'moodle/badges:viewawarded' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), 'moodle/site:forcelanguage' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( ) ), // Perform site-wide search queries through the search API. 'moodle/search:query' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'guest' => CAP_ALLOW, 'user' => CAP_ALLOW, 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // Competencies. 'moodle/competency:competencymanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/competency:competencyview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'user' => CAP_ALLOW ), ), 'moodle/competency:competencygrade' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, // And CONTEXT_USER. 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), ), // Course competencies. 'moodle/competency:coursecompetencymanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), ), 'moodle/competency:coursecompetencyconfigure' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:coursecompetencygradable' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW ), 'clonepermissionsfrom' => 'moodle/course:isincompletionreports' ), 'moodle/competency:coursecompetencyview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'user' => CAP_ALLOW ), ), // Evidence. 'moodle/competency:evidencedelete' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( ), 'clonepermissionsfrom' => 'moodle/site:config' ), // User plans. 'moodle/competency:planmanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:planmanagedraft' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:planmanageown' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( ), ), 'moodle/competency:planmanageowndraft' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( ), ), 'moodle/competency:planview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:planviewdraft' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:planviewown' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ), ), 'moodle/competency:planviewowndraft' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( ), ), 'moodle/competency:planrequestreview' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/competency:planrequestreviewown' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ) ), 'moodle/competency:planreview' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:plancomment' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:plancommentown' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ), ), // User competencies. 'moodle/competency:usercompetencyview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, // And CONTEXT_COURSE. 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'teacher' => CAP_ALLOW ) ), 'moodle/competency:usercompetencyrequestreview' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ) ), 'moodle/competency:usercompetencyrequestreviewown' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ) ), 'moodle/competency:usercompetencyreview' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:usercompetencycomment' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:usercompetencycommentown' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ), ), // Template. 'moodle/competency:templatemanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/analytics:listinsights' => array( 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), 'moodle/analytics:managemodels' => array( 'riskbitmask' => RISK_CONFIG, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:templateview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSECAT, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), // User evidence. 'moodle/competency:userevidencemanage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/competency:userevidencemanageown' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'user' => CAP_ALLOW ), ), 'moodle/competency:userevidenceview' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_USER, 'archetypes' => array( 'manager' => CAP_ALLOW ), ), 'moodle/site:maintenanceaccess' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( ) ), // Allow message any user, regardlesss of the privacy preferences for messaging. 'moodle/site:messageanyuser' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ), // Context locking/unlocking. 'moodle/site:managecontextlocks' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => [ ], ], // Manual completion toggling. 'moodle/course:togglecompletion' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => [ 'user' => CAP_ALLOW, ], ], 'moodle/analytics:listowninsights' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array( 'user' => CAP_ALLOW ) ), // Set display option buttons to an H5P content. 'moodle/h5p:setdisplayoptions' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, ) ), // Allow to deploy H5P content. 'moodle/h5p:deploy' => array( 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Allow to update H5P content-type libraries. 'moodle/h5p:updatelibraries' => [ 'riskbitmask' => RISK_XSS, 'captype' => 'write', 'contextlevel' => CONTEXT_MODULE, 'archetypes' => [ 'manager' => CAP_ALLOW, ] ], // Allow users to recommend activities in the activity chooser. 'moodle/course:recommendactivity' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'manager' => CAP_ALLOW, ] ], // Content bank capabilities. 'moodle/contentbank:access' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), 'moodle/contentbank:upload' => array( 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), // Delete any content from the content bank. 'moodle/contentbank:deleteanycontent' => [ 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, ] ], // Delete content created by yourself. 'moodle/contentbank:deleteowncontent' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'user' => CAP_ALLOW, ] ], // Manage (rename, move, publish, share, etc.) any content from the content bank. 'moodle/contentbank:manageanycontent' => [ 'riskbitmask' => RISK_DATALOSS, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, ) ], // Manage (rename, move, publish, share, etc.) content created by yourself. 'moodle/contentbank:manageowncontent' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ], // Allow users to create/edit content within the content bank. 'moodle/contentbank:useeditor' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ], // Allow users to download content. 'moodle/contentbank:downloadcontent' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ] ], // Allow users to copy content. 'moodle/contentbank:copyanycontent' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, ] ], // Allow users to copy content. 'moodle/contentbank:copycontent' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ] ], 'moodle/contentbank:configurecustomfields' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, ], ], 'moodle/contentbank:changelockedcustomfields' => [ 'riskbitmask' => RISK_SPAM, 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, ], ], // Allow users to download course content. 'moodle/course:downloadcoursecontent' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'student' => CAP_ALLOW, 'teacher' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ) ], // Allow users to configure download course content functionality within a course, if the feature is available. 'moodle/course:configuredownloadcontent' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW ), ], // Allow to manage payment accounts. 'moodle/payment:manageaccounts' => [ 'captype' => 'write', 'riskbitmask' => RISK_PERSONAL | RISK_CONFIG | RISK_DATALOSS, 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [], ], // Allow to view payments. 'moodle/payment:viewpayments' => [ 'captype' => 'read', 'riskbitmask' => RISK_PERSONAL, 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [], ], // Allow users to view hidden content. 'moodle/contentbank:viewunlistedcontent' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'manager' => CAP_ALLOW, 'coursecreator' => CAP_ALLOW, ] ], // Allow users to view custom reports. 'moodle/reportbuilder:view' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'user' => CAP_ALLOW, ], ], // Allow users to view all custom reports. 'moodle/reportbuilder:viewall' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [], ], // Allow users to create/edit their own custom reports. 'moodle/reportbuilder:edit' => [ 'captype' => 'write', 'riskbitmap' => RISK_PERSONAL, 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'manager' => CAP_ALLOW, ], ], // Allow users to create/edit all custom reports. 'moodle/reportbuilder:editall' => [ 'captype' => 'write', 'riskbitmap' => RISK_PERSONAL, 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [], ], // Allow users to configure custom fields for custom reports. 'moodle/reportbuilder:configurecustomfields' => [ 'captype' => 'write', 'riskbitmap' => RISK_PERSONAL, 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [], ], // Allow users to schedule reports as other users. 'moodle/reportbuilder:scheduleviewas' => [ 'captype' => 'read', 'riskbitmap' => RISK_PERSONAL, 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [], ], // Allow users to share activities to MoodleNet. 'moodle/moodlenet:shareactivity' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ] ], // Allow users to configure course communication rooms. 'moodle/course:configurecoursecommunication' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ] ], // Allow users to share courses to MoodleNet. 'moodle/moodlenet:sharecourse' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ] ], // Allow users to edit course welcome messages. 'moodle/course:editcoursewelcomemessage' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => [ 'editingteacher' => CAP_ALLOW, 'manager' => CAP_ALLOW, ], ], // Set a users acceptance of the AI policy. 'moodle/ai:fetchanyuserpolicystatus' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'user' => CAP_ALLOW, ], ], // Set a users acceptance of the AI policy. 'moodle/ai:acceptpolicy' => [ 'captype' => 'write', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'user' => CAP_ALLOW, ], ], // Get a users acceptance of the AI policy. 'moodle/ai:fetchpolicy' => [ 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'user' => CAP_ALLOW, ], ], // Allow managers to view the AI policy acceptance report. 'moodle/ai:viewaipolicyacceptancereport' => [ 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'manager' => CAP_ALLOW, ], ], // Allow managers to view the AI usage report. 'moodle/ai:viewaiusagereport' => [ 'riskbitmask' => RISK_PERSONAL, 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => [ 'manager' => CAP_ALLOW, ], ], ); install.xml000064400001404465152266401200006750 0ustar00
events.php000064400000005337152266451710006602 0ustar00. /** * Definition of core event observers. * * The observers defined in this file are notified when respective events are triggered. All plugins * support this. * * For more information, take a look to the documentation available: * - Events API: {@link https://docs.moodle.org/dev/Events_API} * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @package core * @category event * @copyright 2007 onwards Martin Dougiamas http://dougiamas.com * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); // List of legacy event handlers. $handlers = array( // No more old events! ); // List of events_2 observers. $observers = array( array( 'eventname' => '\core\event\course_module_completion_updated', 'callback' => 'core_badges_observer::course_module_criteria_review', ), array( 'eventname' => '\core\event\badge_awarded', 'callback' => 'core_badges_observer::badge_criteria_review', ), array( 'eventname' => '\core\event\course_completed', 'callback' => 'core_badges_observer::course_criteria_review', ), array( 'eventname' => '\core\event\user_updated', 'callback' => 'core_badges_observer::profile_criteria_review', ), array( 'eventname' => '\core\event\cohort_member_added', 'callback' => 'core_badges_observer::cohort_criteria_review', ), array( 'eventname' => '\core\event\competency_evidence_created', 'callback' => 'core_badges_observer::competency_criteria_review', ), // Competencies. array( 'eventname' => '\core\event\course_completed', 'callback' => 'core_competency\api::observe_course_completed', ), array( 'eventname' => '\core\event\course_module_completion_updated', 'callback' => 'core_competency\api::observe_course_module_completion_updated', ), ); // List of all events triggered by Moodle can be found using Events list report. caches.php000064400000055052152266527160006526 0ustar00. /** * Core cache definitions. * * This file is part of Moodle's cache API, affectionately called MUC. * It contains the components that are requried in order to use caching. * * @package core * @category cache * @copyright 2012 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $definitions = array( // Used to store processed lang files. // The keys used are the revision, lang and component of the string file. // The static acceleration size has been based upon student access of the site. 'string' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 30, 'canuselocalstore' => true, ), // Used to store cache of all available translations. 'langmenu' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'canuselocalstore' => true, ), // Used to store database meta information. // The database meta information includes information about tables and there columns. // Its keys are the table names. // When creating an instance of this definition you must provide the database family that is being used. 'databasemeta' => array( 'mode' => cache_store::MODE_APPLICATION, 'requireidentifiers' => array( 'dbfamily' ), 'simpledata' => true, // This is a read only class, so leaving references in place is safe. 'staticacceleration' => true, 'staticaccelerationsize' => 15 ), // Event invalidation cache. // This cache is used to manage event invalidation, its keys are the event names. // Whenever something is invalidated it is both purged immediately and an event record created with the timestamp. // When a new cache is initialised all timestamps are looked at and if past data is once more invalidated. // Data guarantee is required in order to ensure invalidation always occurs. // Persistence has been turned on as normally events are used for frequently used caches and this event invalidation // cache will likely be used either lots or never. 'eventinvalidation' => array( 'mode' => cache_store::MODE_APPLICATION, 'staticacceleration' => true, 'requiredataguarantee' => true, 'simpledata' => true, ), // Cache for question definitions. This is used by the question_bank class. // Users probably do not need to know about this cache. They will just call // question_bank::load_question. 'questiondata' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // The id of the question is used. 'requiredataguarantee' => false, 'datasource' => 'question_finder', 'datasourcefile' => 'question/engine/bank.php', ), // HTML Purifier cache // This caches the html purifier cleaned text. This is done because the text is usually cleaned once for every user // and context combo. Text caching handles caching for the combination, this cache is responsible for caching the // cleaned text which is shareable. 'htmlpurifier' => array( 'mode' => cache_store::MODE_APPLICATION, 'canuselocalstore' => true, ), // Used to store data from the config + config_plugins table in the database. // The key used is the component: // - core for all core config settings // - plugin component for all plugin settings. // Persistence is used because normally several settings within a script. 'config' => array( 'mode' => cache_store::MODE_APPLICATION, 'staticacceleration' => true, 'simpledata' => true ), // Groupings belonging to a course. // A simple cache designed to replace $GROUPLIB_CACHE->groupings. // Items are organised by course id and are essentially course records. 'groupdata' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // The course id the groupings exist for. 'simpledata' => true, // Array of stdClass objects containing only strings. 'staticacceleration' => true, // Likely there will be a couple of calls to this. 'staticaccelerationsize' => 2, // The original cache used 1, we've increased that to two. ), // Whether a course currently has hidden groups. 'coursehiddengroups' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // The course id the groupings exist for. 'simpledata' => true, // Booleans. 'staticacceleration' => true, // Likely there will be a couple of calls to this. ), // Used to cache calendar subscriptions. 'calendar_subscriptions' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, ), // Cache the course categories where the user has any enrolment and all categories that this user can manage. 'calendar_categories' => array( 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array( 'changesincoursecat', 'changesincategoryenrolment', ), 'ttl' => 900, ), // Cache the capabilities list DB table. See get_all_capabilities and get_deprecated_capability_info in accesslib. 'capabilities' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 2, // Should be main capabilities and deprecated capabilities. 'ttl' => 3600, // Just in case. ), // YUI Module cache. // This stores the YUI module metadata for Shifted YUI modules in Moodle. 'yuimodules' => array( 'mode' => cache_store::MODE_APPLICATION, ), // Cache for the list of event observers. 'observers' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 2, ), // Cache used by the {@link core_plugin_manager} class. // NOTE: this must be a shared cache. 'plugin_manager' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, ), // Used to store the full tree of course categories. 'coursecattree' => array( 'mode' => cache_store::MODE_APPLICATION, 'staticacceleration' => true, 'invalidationevents' => array( 'changesincoursecat', ) ), // Used to store data for course categories visible to current user. Helps to browse list of categories. 'coursecat' => array( 'mode' => cache_store::MODE_SESSION, 'invalidationevents' => array( 'changesincoursecat', 'changesincourse', ), 'ttl' => 600, ), // Used to store data for course categories visible to current user. Helps to browse list of categories. 'coursecatrecords' => array( 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'invalidationevents' => array( 'changesincoursecat', ), ), // Used to store state of sections in course (collapsed or not). 'coursesectionspreferences' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => false, 'staticacceleration' => true, ], // Cache course contacts for the courses. 'coursecontacts' => array( 'mode' => cache_store::MODE_APPLICATION, 'staticacceleration' => true, 'simplekeys' => true, 'ttl' => 3600, ), // Course reactive state cache. 'courseeditorstate' => [ 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => [ 'changesincoursestate', ], ], // Course actions instances cache. 'courseactionsinstances' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => false, 'staticacceleration' => true, // Executing actions in more than 10 courses usually means executing the same action on each course // so there is no need for caching individual course instances. 'staticaccelerationsize' => 10, ], // Used to store data for repositories to avoid repetitive DB queries within one request. 'repositories' => array( 'mode' => cache_store::MODE_REQUEST, ), // Used to store external badges. 'externalbadges' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'ttl' => 3600, ), // Accumulated information about course modules and sections used to print course view page (user-independent). // Used in functions: // - course_modinfo::build_course_section_cache() // - course_modinfo::inner_build_course_cache() // - get_array_of_activities() // Reset/update in functions: // - rebuild_course_cache() // - course_modinfo::purge_module_cache() // - course_modinfo::purge_section_cache() // - remove_course_contents(). 'coursemodinfo' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'canuselocalstore' => true, 'requirelockingbeforewrite' => true ), // This is the session user selections cache. // It's a special cache that is used to record user selections that should persist for the lifetime of the session. // Things such as which categories the user has expanded can be stored here. // It uses simple keys and simple data, please ensure all uses conform to those two constraints. 'userselections' => array( 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true ), // Used to cache activity completion status. 'completion' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'ttl' => 3600, 'staticacceleration' => true, 'staticaccelerationsize' => 2, // Should be current course and site course. ), // Used to cache course completion status. 'coursecompletion' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'ttl' => 3600, 'staticacceleration' => true, 'staticaccelerationsize' => 30, // Will be users list of current courses in nav. ), // A simple cache that stores whether a user can expand a course in the navigation. // The key is the course ID and the value will either be 1 or 0 (cast to bool). // The cache isn't always up to date, it should only ever be used to save a costly call to // can_access_course on the first page request a user makes. 'navigation_expandcourse' => array( 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true ), // Caches suspended userids by course. // The key is the courseid, the value is an array of user ids. 'suspended_userids' => array( 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => true, ), // Cache system-wide role definitions. 'roledefs' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 30, ), // Caches plugins existing functions by function name and file. // Set static acceleration size to 5 to load a few functions. 'plugin_functions' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'canuselocalstore' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 5 ), // Caches data about tag collections and areas. 'tags' => array( 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'staticacceleration' => true, ), // Grade categories. Stored at session level as invalidation is very aggressive. 'grade_categories' => array( 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'invalidationevents' => array( 'changesingradecategories', ) ), // Store temporary tables information. 'temp_tables' => array( 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => true ), // Caches tag index builder results. 'tagindexbuilder' => array( 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simplevalues' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 10, 'ttl' => 900, // 15 minutes. 'invalidationevents' => array( 'resettagindexbuilder', ), ), // Caches contexts with insights. 'contextwithinsights' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 1 ), // Caches message processors. 'message_processors_enabled' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 3 ), // Caches the time of the last message in a conversation. 'message_time_last_message_between_users' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // The conversation id is used. 'simplevalues' => true, 'datasource' => '\core_message\time_last_message_between_users', ), // Caches font awesome icons. 'fontawesomeiconmapping' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 1 ), // Caches processed CSS. 'postprocessedcss' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => false, ), // Caches grouping and group ids of a user. 'user_group_groupings' => array( 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, ), // This is the user's pre sign-up session cache. // This cache is used to record the user's pre sign-up data such as // age of digital consent (minor) status, accepted policies, etc. 'presignup' => array( 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'ttl' => 1800 ), // Caches the first time we analysed models' analysables. 'modelfirstanalyses' => array( 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => true, ), // Cache the list of portfolio instances for the logged in user // in the portfolio_add_button constructor to avoid loading the // same data multiple times. 'portfolio_add_button_portfolio_instances' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'staticacceleration' => true ], // Cache the user dates for courses set to relative dates mode. 'course_user_dates' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true ], // Information generated during the calculation of indicators. 'calculablesinfo' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => false, 'simpledata' => false, ], // The list of content items (activities, resources and their subtypes) that can be added to a course for a user. 'user_course_content_items' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, ], // The list of favourited content items (activities, resources and their subtypes) for a user. 'user_favourite_course_content_items' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, ], \core_course\local\service\content_item_service::RECOMMENDATION_CACHE => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, ], // Caches contentbank extensions management. 'contentbank_enabled_extensions' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, ], 'contentbank_context_extensions' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, ], // Language strings for H5P content-type libraries. // Key "{$libraryname}/{$language}"" contains translations for a given library and language. // Key "$libraryname" has a list of all of the available languages for the library. 'h5p_content_type_translations' => [ 'mode' => cache_store::MODE_APPLICATION, 'simpledata' => true, ], // File cache for H5P Library ids. 'h5p_libraries' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'canuselocalstore' => true ], // File cache for H5P Library files. 'h5p_library_files' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'canuselocalstore' => true ], // Cache the grade letters for faster retrival. 'grade_letters' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 100 ], // Cache for licenses. 'license' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => false, ], // Cache the grade setting for faster retrieval. 'gradesetting' => [ 'mode' => cache_store::MODE_REQUEST, 'simplekeys' => true, 'staticacceleration' => true, 'staticaccelerationsize' => 100 ], // Course image cache. 'course_image' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'datasource' => '\core_course\cache\course_image', ], // Cache the course categories where the user has access the content bank. 'contentbank_allowed_categories' => [ 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => [ 'changesincoursecat', 'changesincategoryenrolment', ], ], // Cache the courses where the user has access the content bank. 'contentbank_allowed_courses' => [ 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => [ 'changesincoursecat', 'changesincategoryenrolment', 'changesincourse', ], ], // Users allowed reports according to audience. 'reportbuilder_allowed_reports' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'ttl' => 1800, ], // Cache image dimensions. 'file_imageinfo' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, 'canuselocalstore' => true, 'staticaccelerationsize' => 100, ], // Cache if a user has the capability to share to MoodleNet. 'moodlenet_usercanshare' => [ 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'ttl' => 1800, 'invalidationevents' => [ 'changesincoursecat', 'changesincategoryenrolment', 'changesincourse', ], ], // A theme has been used in context to override the default theme. // Applies to user, cohort, category and course. 'theme_usedincontext' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true, ], 'routes' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, 'simpledata' => true, 'canuselocalstore' => true, ], // Cache to store user AI policy acceptance status. 'ai_policy' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // Cache must use simple keys (a-zA-Z0-9_). 'simpledata' => true, // Cache stores integer values which are simple data. 'staticacceleration' => true, 'datasource' => \core_ai\cache\policy::class, 'canuselocalstore' => true, ], // Cache to store AI rate limits. // Used by AI provider plugins to limit the number of requests to external services. 'ai_ratelimit' => [ 'mode' => cache_store::MODE_APPLICATION, 'simplekeys' => true, // Cache must use simple keys (a-zA-Z0-9_). 'simpledata' => true, // Cache stores integer values which are simple data. 'staticacceleration' => true, ], // The navigation_cache class used this cache to store the navigation nodes. 'navigation_cache' => [ 'mode' => cache_store::MODE_SESSION, 'simplekeys' => true, 'simpledata' => true, 'ttl' => 1800, ], ); upgradelib.php000064400000255236152266563550007427 0ustar00. /** * Upgrade helper functions * * This file is used for special upgrade functions - for example groups and gradebook. * These functions must use SQL and database related functions only- no other Moodle API, * because it might depend on db structures that are not yet present during upgrade. * (Do not use functions from accesslib.php, grades classes or group functions at all!) * * @package core_install * @category upgrade * @copyright 2007 Petr Skoda (http://skodak.org) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Returns all non-view and non-temp tables with sane names. * Prints list of non-supported tables using $OUTPUT->notification() * * @return array */ function upgrade_mysql_get_supported_tables() { global $OUTPUT, $DB; $tables = array(); $patprefix = str_replace('_', '\\_', $DB->get_prefix()); $pregprefix = preg_quote($DB->get_prefix(), '/'); $sql = "SHOW FULL TABLES LIKE '$patprefix%'"; $rs = $DB->get_recordset_sql($sql); foreach ($rs as $record) { $record = array_change_key_case((array)$record, CASE_LOWER); $type = $record['table_type']; unset($record['table_type']); $fullname = array_shift($record); if ($pregprefix === '') { $name = $fullname; } else { $count = null; $name = preg_replace("/^$pregprefix/", '', $fullname, -1, $count); if ($count !== 1) { continue; } } if (!preg_match("/^[a-z][a-z0-9_]*$/", $name)) { echo $OUTPUT->notification("Database table with invalid name '$fullname' detected, skipping.", 'notifyproblem'); continue; } if ($type === 'VIEW') { echo $OUTPUT->notification("Unsupported database table view '$fullname' detected, skipping.", 'notifyproblem'); continue; } $tables[$name] = $name; } $rs->close(); return $tables; } /** * Using data for a single course-module that has groupmembersonly enabled, * returns the new availability value that incorporates the correct * groupmembersonly option. * * Included as a function so that it can be shared between upgrade and restore, * and unit-tested. * * @param int $groupingid Grouping id for the course-module (0 if none) * @param string $availability Availability JSON data for the module (null if none) * @return string New value for availability for the module */ function upgrade_group_members_only($groupingid, $availability) { // Work out the new JSON object representing this option. if ($groupingid) { // Require specific grouping. $condition = (object)array('type' => 'grouping', 'id' => (int)$groupingid); } else { // No grouping specified, so require membership of any group. $condition = (object)array('type' => 'group'); } if (is_null($availability)) { // If there are no conditions using the new API then just set it. $tree = (object)array('op' => '&', 'c' => array($condition), 'showc' => array(false)); } else { // There are existing conditions. $tree = json_decode($availability); switch ($tree->op) { case '&' : // For & conditions we can just add this one. $tree->c[] = $condition; $tree->showc[] = false; break; case '!|' : // For 'not or' conditions we can add this one // but negated. $tree->c[] = (object)array('op' => '!&', 'c' => array($condition)); $tree->showc[] = false; break; default: // For the other two (OR and NOT AND) we have to add // an extra level to the tree. $tree = (object)array('op' => '&', 'c' => array($tree, $condition), 'showc' => array($tree->show, false)); // Inner trees do not have a show option, so remove it. unset($tree->c[0]->show); break; } } return json_encode($tree); } /** * Marks all courses with changes in extra credit weight calculation * * Used during upgrade and in course restore process * * This upgrade script is needed because we changed the algorithm for calculating the automatic weights of extra * credit items and want to prevent changes in the existing student grades. * * @param int $onlycourseid */ function upgrade_extra_credit_weightoverride($onlycourseid = 0) { global $DB; // Find all courses that have categories in Natural aggregation method where there is at least one extra credit // item and at least one item with overridden weight. $courses = $DB->get_fieldset_sql( "SELECT DISTINCT gc.courseid FROM {grade_categories} gc INNER JOIN {grade_items} gi ON gc.id = gi.categoryid AND gi.weightoverride = :weightoverriden INNER JOIN {grade_items} gie ON gc.id = gie.categoryid AND gie.aggregationcoef = :extracredit WHERE gc.aggregation = :naturalaggmethod" . ($onlycourseid ? " AND gc.courseid = :onlycourseid" : ''), array('naturalaggmethod' => 13, 'weightoverriden' => 1, 'extracredit' => 1, 'onlycourseid' => $onlycourseid, ) ); foreach ($courses as $courseid) { $gradebookfreeze = get_config('core', 'gradebook_calculations_freeze_' . $courseid); if (!$gradebookfreeze) { set_config('gradebook_calculations_freeze_' . $courseid, 20150619); } } } /** * Marks all courses that require calculated grade items be updated. * * Used during upgrade and in course restore process. * * This upgrade script is needed because the calculated grade items were stuck with a maximum of 100 and could be changed. * This flags the courses that are affected and the grade book is frozen to retain grade integrity. * * @param int $courseid Specify a course ID to run this script on just one course. */ function upgrade_calculated_grade_items($courseid = null) { global $DB, $CFG; $affectedcourses = array(); $possiblecourseids = array(); $params = array(); $singlecoursesql = ''; if (isset($courseid)) { $singlecoursesql = "AND ns.id = :courseid"; $params['courseid'] = $courseid; } $siteminmaxtouse = 1; if (isset($CFG->grade_minmaxtouse)) { $siteminmaxtouse = $CFG->grade_minmaxtouse; } $courseidsql = "SELECT ns.id FROM ( SELECT c.id, coalesce(" . $DB->sql_compare_text('gs.value') . ", :siteminmax) AS gradevalue FROM {course} c LEFT JOIN {grade_settings} gs ON c.id = gs.courseid AND ((gs.name = 'minmaxtouse' AND " . $DB->sql_compare_text('gs.value') . " = '2')) ) ns WHERE " . $DB->sql_compare_text('ns.gradevalue') . " = '2' $singlecoursesql"; $params['siteminmax'] = $siteminmaxtouse; $courses = $DB->get_records_sql($courseidsql, $params); foreach ($courses as $course) { $possiblecourseids[$course->id] = $course->id; } if (!empty($possiblecourseids)) { [$sql, $params] = $DB->get_in_or_equal($possiblecourseids); // A calculated grade item grade min != 0 and grade max != 100 and the course setting is set to // "Initial min and max grades". $coursesql = "SELECT DISTINCT courseid FROM {grade_items} WHERE calculation IS NOT NULL AND itemtype = 'manual' AND (grademax <> 100 OR grademin <> 0) AND courseid $sql"; $affectedcourses = $DB->get_records_sql($coursesql, $params); } // Check for second type of affected courses. // If we already have the courseid parameter set in the affectedcourses then there is no need to run through this section. if (!isset($courseid) || !in_array($courseid, $affectedcourses)) { $singlecoursesql = ''; $params = array(); if (isset($courseid)) { $singlecoursesql = "AND courseid = :courseid"; $params['courseid'] = $courseid; } $nestedsql = "SELECT id FROM {grade_items} WHERE itemtype = 'category' AND calculation IS NOT NULL $singlecoursesql"; $calculatedgradecategories = $DB->get_records_sql($nestedsql, $params); $categoryids = array(); foreach ($calculatedgradecategories as $key => $gradecategory) { $categoryids[$key] = $gradecategory->id; } if (!empty($categoryids)) { [$sql, $params] = $DB->get_in_or_equal($categoryids); // A category with a calculation where the raw grade min and the raw grade max don't match the grade min and grade max // for the category. $coursesql = "SELECT DISTINCT gi.courseid FROM {grade_grades} gg, {grade_items} gi WHERE gi.id = gg.itemid AND (gg.rawgrademax <> gi.grademax OR gg.rawgrademin <> gi.grademin) AND gi.id $sql"; $additionalcourses = $DB->get_records_sql($coursesql, $params); foreach ($additionalcourses as $key => $additionalcourse) { if (!array_key_exists($key, $affectedcourses)) { $affectedcourses[$key] = $additionalcourse; } } } } foreach ($affectedcourses as $affectedcourseid) { if (isset($CFG->upgrade_calculatedgradeitemsonlyregrade) && !($courseid)) { $DB->set_field('grade_items', 'needsupdate', 1, array('courseid' => $affectedcourseid->courseid)); } else { // Check to see if the gradebook freeze is already in affect. $gradebookfreeze = get_config('core', 'gradebook_calculations_freeze_' . $affectedcourseid->courseid); if (!$gradebookfreeze) { set_config('gradebook_calculations_freeze_' . $affectedcourseid->courseid, 20150627); } } } } /** * This function creates a default separated/connected scale * so there's something in the database. * * @global object * @return void */ function make_default_scale() { global $DB; $defaultscale = new stdClass(); $defaultscale->courseid = 0; $defaultscale->userid = 0; $defaultscale->name = get_string('separateandconnected'); $defaultscale->description = get_string('separateandconnectedinfo'); $defaultscale->scale = get_string('separateandconnected1') . ',' . get_string('separateandconnected2') . ',' . get_string('separateandconnected3'); $defaultscale->timemodified = time(); $defaultscale->id = $DB->insert_record('scale', $defaultscale); return $defaultscale; } /** * Create another default scale. * * @param int $oldversion * @return bool always true */ function make_competence_scale() { global $DB; $defaultscale = new stdClass(); $defaultscale->courseid = 0; $defaultscale->userid = 0; $defaultscale->name = get_string('defaultcompetencescale'); $defaultscale->description = get_string('defaultcompetencescaledesc'); $defaultscale->scale = get_string('defaultcompetencescalenotproficient').','. get_string('defaultcompetencescaleproficient'); $defaultscale->timemodified = time(); $defaultscale->id = $DB->insert_record('scale', $defaultscale); return $defaultscale; } /** * Marks all courses that require rounded grade items be updated. * * Used during upgrade and in course restore process. * * This upgrade script is needed because it has been decided that if a grade is rounded up, and it will changed a letter * grade or satisfy a course completion grade criteria, then it should be set as so, and the letter will be awarded and or * the course completion grade will be awarded. * * @param int $courseid Specify a course ID to run this script on just one course. */ function upgrade_course_letter_boundary($courseid = null) { global $DB, $CFG; $coursesql = ''; $params = array('contextlevel' => CONTEXT_COURSE); if (!empty($courseid)) { $coursesql = 'AND c.id = :courseid'; $params['courseid'] = $courseid; } // Check to see if the system letter boundaries are borked. $systemcontext = context_system::instance(); $systemneedsfreeze = upgrade_letter_boundary_needs_freeze($systemcontext); // Check the setting for showing the letter grade in a column (default is false). $usergradelettercolumnsetting = 0; if (isset($CFG->grade_report_user_showlettergrade)) { $usergradelettercolumnsetting = (int)$CFG->grade_report_user_showlettergrade; } $lettercolumnsql = ''; if ($usergradelettercolumnsetting) { // The system default is to show a column with letters (and the course uses the defaults). $lettercolumnsql = '(gss.value is NULL OR ' . $DB->sql_compare_text('gss.value') . ' <> \'0\')'; } else { // The course displays a column with letters. $lettercolumnsql = $DB->sql_compare_text('gss.value') . ' = \'1\''; } // 3, 13, 23, 31, and 32 are the grade display types that incorporate showing letters. See lib/grade/constants/php. $systemusesletters = (int) (isset($CFG->grade_displaytype) && in_array($CFG->grade_displaytype, array(3, 13, 23, 31, 32))); $systemletters = $systemusesletters || $usergradelettercolumnsetting; $contextselect = context_helper::get_preload_record_columns_sql('ctx'); if ($systemletters && $systemneedsfreeze) { // Select courses with no grade setting for display and a grade item that is using the default display, // but have not altered the course letter boundary configuration. These courses are definitely affected. $sql = "SELECT DISTINCT c.id AS courseid FROM {course} c JOIN {grade_items} gi ON c.id = gi.courseid JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel LEFT JOIN {grade_settings} gs ON gs.courseid = c.id AND gs.name = 'displaytype' LEFT JOIN {grade_settings} gss ON gss.courseid = c.id AND gss.name = 'report_user_showlettergrade' LEFT JOIN {grade_letters} gl ON gl.contextid = ctx.id WHERE gi.display = 0 AND ((gs.value is NULL) AND ($lettercolumnsql)) AND gl.id is NULL $coursesql"; $affectedcourseids = $DB->get_recordset_sql($sql, $params); foreach ($affectedcourseids as $courseid) { set_config('gradebook_calculations_freeze_' . $courseid->courseid, 20160518); } $affectedcourseids->close(); } // If the system letter boundary is okay proceed to check grade item and course grade display settings. $sql = "SELECT DISTINCT c.id AS courseid, $contextselect FROM {course} c JOIN {context} ctx ON ctx.instanceid = c.id AND ctx.contextlevel = :contextlevel JOIN {grade_items} gi ON c.id = gi.courseid LEFT JOIN {grade_settings} gs ON c.id = gs.courseid AND gs.name = 'displaytype' LEFT JOIN {grade_settings} gss ON gss.courseid = c.id AND gss.name = 'report_user_showlettergrade' WHERE ( -- A grade item is using letters (gi.display IN (3, 13, 23, 31, 32)) -- OR the course is using letters OR (" . $DB->sql_compare_text('gs.value') . " IN ('3', '13', '23', '31', '32') -- OR the course using the system default which is letters OR (gs.value IS NULL AND $systemusesletters = 1) ) OR ($lettercolumnsql) ) -- AND the course matches $coursesql"; $potentialcourses = $DB->get_recordset_sql($sql, $params); foreach ($potentialcourses as $value) { $gradebookfreeze = 'gradebook_calculations_freeze_' . $value->courseid; // Check also if this course id has already been frozen. // If we already have this course ID then move on to the next record. if (!property_exists($CFG, $gradebookfreeze)) { // Check for 57 letter grade issue. context_helper::preload_from_record($value); $coursecontext = context_course::instance($value->courseid); if (upgrade_letter_boundary_needs_freeze($coursecontext)) { // We have a course with a possible score standardisation problem. Flag for freeze. // Flag this course as being frozen. set_config('gradebook_calculations_freeze_' . $value->courseid, 20160518); } } } $potentialcourses->close(); } /** * Checks the letter boundary of the provided context to see if it needs freezing. * Each letter boundary is tested to see if receiving that boundary number will * result in achieving the cosponsoring letter. * * @param object $context Context object * @return bool if the letter boundary for this context should be frozen. */ function upgrade_letter_boundary_needs_freeze($context) { global $DB; $contexts = $context->get_parent_context_ids(); array_unshift($contexts, $context->id); foreach ($contexts as $ctxid) { $letters = $DB->get_records_menu('grade_letters', array('contextid' => $ctxid), 'lowerboundary DESC', 'lowerboundary, letter'); if (!empty($letters)) { foreach ($letters as $boundary => $notused) { $standardisedboundary = upgrade_standardise_score($boundary, 0, 100, 0, 100); if ($standardisedboundary < $boundary) { return true; } } // We found letters but we have no boundary problem. return false; } } return false; } /** * Given a float value situated between a source minimum and a source maximum, converts it to the * corresponding value situated between a target minimum and a target maximum. Thanks to Darlene * for the formula :-) * * @param float $rawgrade * @param float $sourcemin * @param float $sourcemax * @param float $targetmin * @param float $targetmax * @return float Converted value */ function upgrade_standardise_score($rawgrade, $sourcemin, $sourcemax, $targetmin, $targetmax) { if (is_null($rawgrade)) { return null; } if ($sourcemax == $sourcemin or $targetmin == $targetmax) { // Prevent division by 0. return $targetmax; } $factor = ($rawgrade - $sourcemin) / ($sourcemax - $sourcemin); $diff = $targetmax - $targetmin; $standardisedvalue = $factor * $diff + $targetmin; return $standardisedvalue; } /** * Provides a way to check and update a serialized string that uses the deprecated object class. * * @param string $serializeddata Serialized string which may contain the now deprecated object. * @return array Returns an array where the first variable is a bool with a status of whether the initial data was changed * or not. The second variable is the said data. */ function upgrade_fix_serialized_objects($serializeddata) { $updated = false; if (strpos($serializeddata, ":6:\"object") !== false) { $serializeddata = str_replace(":6:\"object", ":8:\"stdClass", $serializeddata); $updated = true; } return [$updated, $serializeddata]; } /** * Deletes file records which have their repository deleted. * */ function upgrade_delete_orphaned_file_records() { global $DB; $sql = "SELECT f.id, f.contextid, f.component, f.filearea, f.itemid, fr.id AS referencefileid FROM {files} f JOIN {files_reference} fr ON f.referencefileid = fr.id LEFT JOIN {repository_instances} ri ON fr.repositoryid = ri.id WHERE ri.id IS NULL"; $deletedfiles = $DB->get_recordset_sql($sql); $deletedfileids = array(); $fs = get_file_storage(); foreach ($deletedfiles as $deletedfile) { $fs->delete_area_files($deletedfile->contextid, $deletedfile->component, $deletedfile->filearea, $deletedfile->itemid); $deletedfileids[] = $deletedfile->referencefileid; } $deletedfiles->close(); $DB->delete_records_list('files_reference', 'id', $deletedfileids); } /** * Upgrade core licenses shipped with Moodle. */ function upgrade_core_licenses() { global $CFG, $DB; $expectedlicenses = json_decode(file_get_contents($CFG->dirroot . '/lib/licenses.json'))->licenses; if (!is_array($expectedlicenses)) { $expectedlicenses = []; } $corelicenses = $DB->get_records('license', ['custom' => 0]); // Disable core licenses which are no longer current. $todisable = array_diff( array_map(fn ($license) => $license->shortname, $corelicenses), array_map(fn ($license) => $license->shortname, $expectedlicenses), ); // Disable any old *core* license that does not exist in the licenses.json file. if (count($todisable)) { [$where, $params] = $DB->get_in_or_equal($todisable, SQL_PARAMS_NAMED); $DB->set_field_select( 'license', 'enabled', 0, "shortname {$where}", $params ); } // Add any new licenses. foreach ($expectedlicenses as $expectedlicense) { if (!$expectedlicense->enabled) { // Skip any license which is no longer enabled. continue; } if (!$DB->record_exists('license', ['shortname' => $expectedlicense->shortname])) { // If the license replaces an older one, check whether this old license was enabled or not. $isreplacement = false; foreach (array_reverse($expectedlicense->replaces ?? []) as $item) { foreach ($corelicenses as $corelicense) { if ($corelicense->shortname === $item) { $expectedlicense->enabled = $corelicense->enabled; // Also, keep the old sort order. $expectedlicense->sortorder = $corelicense->sortorder * 100; $isreplacement = true; break 2; } } } if (!isset($CFG->upgraderunning) || during_initial_install() || $isreplacement) { // Only install missing core licenses if not upgrading or during initial installation. $DB->insert_record('license', $expectedlicense); } } } // Add/renumber sortorder to all licenses. $licenses = $DB->get_records('license', null, 'sortorder'); $sortorder = 1; foreach ($licenses as $license) { $license->sortorder = $sortorder++; $DB->update_record('license', $license); } // Set the license config values, used by file repository for rendering licenses at front end. $activelicenses = $DB->get_records_menu('license', ['enabled' => 1], 'id', 'id, shortname'); set_config('licenses', implode(',', $activelicenses)); $sitedefaultlicense = get_config('', 'sitedefaultlicense'); if (empty($sitedefaultlicense) || !in_array($sitedefaultlicense, $activelicenses)) { set_config('sitedefaultlicense', reset($activelicenses)); } } /** * Detects if the site may need to get the calendar events fixed or no. With optional output. * * @param bool $output true if the function must output information, false if not. * @return bool true if the site needs to run the fixes, false if not. */ function upgrade_calendar_site_status(bool $output = true): bool { global $DB; // List of upgrade steps where the bug happened. $badsteps = [ '3.9.5' => '2020061504.08', '3.10.2' => '2020110901.09', '3.11dev' => '2021022600.02', '4.0dev' => '2021052500.65', ]; // List of upgrade steps that ran the fixer. $fixsteps = [ '3.9.6+' => '2020061506.05', '3.10.3+' => '2020110903.05', '3.11dev' => '2021042100.02', '4.0dev' => '2021052500.85', ]; $targetsteps = array_merge(array_values($badsteps), array_values( $fixsteps)); [$insql, $inparams] = $DB->get_in_or_equal($targetsteps); $foundsteps = $DB->get_fieldset_sql(" SELECT DISTINCT version FROM {upgrade_log} WHERE plugin = 'core' AND version " . $insql . " ORDER BY version", $inparams); // Analyse the found steps, to decide if the site needs upgrading or no. $badfound = false; $fixfound = false; foreach ($foundsteps as $foundstep) { $badfound = $badfound ?: array_search($foundstep, $badsteps, true); $fixfound = $fixfound ?: array_search($foundstep, $fixsteps, true); } $needsfix = $badfound && !$fixfound; // Let's output some textual information if required to. if ($output) { mtrace(""); if ($badfound) { mtrace("This site has executed the problematic upgrade step {$badsteps[$badfound]} present in {$badfound}."); } else { mtrace("Problematic upgrade steps were NOT found, site should be safe."); } if ($fixfound) { mtrace("This site has executed the fix upgrade step {$fixsteps[$fixfound]} present in {$fixfound}."); } else { mtrace("Fix upgrade steps were NOT found."); } mtrace(""); if ($needsfix) { mtrace("This site NEEDS to run the calendar events fix!"); mtrace(''); mtrace("You can use this CLI tool or upgrade to a version of Moodle that includes"); mtrace("the fix and will be executed as part of the normal upgrade procedure."); mtrace("The following versions or up are known candidates to upgrade to:"); foreach ($fixsteps as $key => $value) { mtrace(" - {$key}: {$value}"); } mtrace(""); } } return $needsfix; } /** * Detects the calendar events needing to be fixed. With optional output. * * @param bool $output true if the function must output information, false if not. * @return stdClass[] an array of event types (as keys) with total and bad counters, plus sql to retrieve them. */ function upgrade_calendar_events_status(bool $output = true): array { global $DB; // Calculate the list of standard (core) activity plugins. $plugins = core_plugin_manager::standard_plugins_list('mod'); $coremodules = "modulename IN ('" . implode("', '", $plugins) . "')"; // Some query parts go here. $brokenevents = "(userid = 0 AND (eventtype <> 'user' OR priority <> 0))"; // From the original bad upgrade step. $standardevents = "(eventtype IN ('site', 'category', 'course', 'group', 'user') AND subscriptionid IS NULL)"; $subscriptionevents = "(subscriptionid IS NOT NULL)"; $overrideevents = "({$coremodules} AND priority IS NOT NULL)"; $actionevents = "({$coremodules} AND instance > 0 and priority IS NULL)"; $otherevents = "(NOT ({$standardevents} OR {$subscriptionevents} OR {$overrideevents} OR {$actionevents}))"; // Detailed query template. $detailstemplate = " SELECT ##group## AS groupname, COUNT(1) AS count FROM {event} WHERE ##groupconditions## GROUP BY ##group##"; // Count total and potentially broken events. $total = $DB->count_records_select('event', ''); $totalbadsql = $brokenevents; $totalbad = $DB->count_records_select('event', $totalbadsql); // Standard events. $standard = $DB->count_records_select('event', $standardevents); $standardbadsql = "{$brokenevents} AND {$standardevents}"; $standardbad = $DB->count_records_select('event', $standardbadsql); $standarddetails = $DB->get_records_sql( str_replace( ['##group##', '##groupconditions##'], ['eventtype', $standardbadsql], $detailstemplate ) ); array_walk($standarddetails, function (&$rec) { $rec = $rec->groupname . ': ' . $rec->count; }); $standarddetails = $standarddetails ? '(' . implode(', ', $standarddetails) . ')' : '- all good!'; // Subscription events. $subscription = $DB->count_records_select('event', $subscriptionevents); $subscriptionbadsql = "{$brokenevents} AND {$subscriptionevents}"; $subscriptionbad = $DB->count_records_select('event', $subscriptionbadsql); $subscriptiondetails = $DB->get_records_sql( str_replace( ['##group##', '##groupconditions##'], ['eventtype', $subscriptionbadsql], $detailstemplate ) ); array_walk($subscriptiondetails, function (&$rec) { $rec = $rec->groupname . ': ' . $rec->count; }); $subscriptiondetails = $subscriptiondetails ? '(' . implode(', ', $subscriptiondetails) . ')' : '- all good!'; // Override events. $override = $DB->count_records_select('event', $overrideevents); $overridebadsql = "{$brokenevents} AND {$overrideevents}"; $overridebad = $DB->count_records_select('event', $overridebadsql); $overridedetails = $DB->get_records_sql( str_replace( ['##group##', '##groupconditions##'], ['modulename', $overridebadsql], $detailstemplate ) ); array_walk($overridedetails, function (&$rec) { $rec = $rec->groupname . ': ' . $rec->count; }); $overridedetails = $overridedetails ? '(' . implode(', ', $overridedetails) . ')' : '- all good!'; // Action events. $action = $DB->count_records_select('event', $actionevents); $actionbadsql = "{$brokenevents} AND {$actionevents}"; $actionbad = $DB->count_records_select('event', $actionbadsql); $actiondetails = $DB->get_records_sql( str_replace( ['##group##', '##groupconditions##'], ['modulename', $actionbadsql], $detailstemplate ) ); array_walk($actiondetails, function (&$rec) { $rec = $rec->groupname . ': ' . $rec->count; }); $actiondetails = $actiondetails ? '(' . implode(', ', $actiondetails) . ')' : '- all good!'; // Other events. $other = $DB->count_records_select('event', $otherevents); $otherbadsql = "{$brokenevents} AND {$otherevents}"; $otherbad = $DB->count_records_select('event', $otherbadsql); $otherdetails = $DB->get_records_sql( str_replace( ['##group##', '##groupconditions##'], ['COALESCE(component, modulename)', $otherbadsql], $detailstemplate ) ); array_walk($otherdetails, function (&$rec) { $rec = ($rec->groupname ?: 'unknown') . ': ' . $rec->count; }); $otherdetails = $otherdetails ? '(' . implode(', ', $otherdetails) . ')' : '- all good!'; // Let's output some textual information if required to. if ($output) { mtrace(""); mtrace("Totals: {$total} / {$totalbad} (total / wrong)"); mtrace(" - standards events: {$standard} / {$standardbad} {$standarddetails}"); mtrace(" - subscription events: {$subscription} / {$subscriptionbad} {$subscriptiondetails}"); mtrace(" - override events: {$override} / {$overridebad} {$overridedetails}"); mtrace(" - action events: {$action} / {$actionbad} {$actiondetails}"); mtrace(" - other events: {$other} / {$otherbad} {$otherdetails}"); mtrace(""); } return [ 'total' => (object)['count' => $total, 'bad' => $totalbad, 'sql' => $totalbadsql], 'standard' => (object)['count' => $standard, 'bad' => $standardbad, 'sql' => $standardbadsql], 'subscription' => (object)['count' => $subscription, 'bad' => $subscriptionbad, 'sql' => $subscriptionbadsql], 'override' => (object)['count' => $override, 'bad' => $overridebad, 'sql' => $overridebadsql], 'action' => (object)['count' => $action, 'bad' => $actionbad, 'sql' => $actionbadsql], 'other' => (object)['count' => $other, 'bad' => $otherbad, 'sql' => $otherbadsql], ]; } /** * Detects the calendar events needing to be fixed. With optional output. * * @param stdClass[] an array of event types (as keys) with total and bad counters, plus sql to retrieve them. * @param bool $output true if the function must output information, false if not. * @param int $maxseconds Number of seconds the function will run as max, with zero meaning no limit. * @return bool true if the function has not finished fixing everything, false if it has finished. */ function upgrade_calendar_events_fix_remaining(array $info, bool $output = true, int $maxseconds = 0): bool { global $DB; upgrade_calendar_events_mtrace('', $output); // Initial preparations. $starttime = time(); $endtime = $maxseconds ? ($starttime + $maxseconds) : 0; // No bad events, or all bad events are "other" events, finished. if ($info['total']->bad == 0 || $info['total']->bad == $info['other']->bad) { return false; } // Let's fix overriden events first (they are the ones performing worse with the missing userid). if ($info['override']->bad != 0) { if (upgrade_calendar_override_events_fix($info['override'], $output, $endtime)) { return true; // Not finished yet. } } // Let's fix the subscription events (like standard ones, but with the event_subscriptions table). if ($info['subscription']->bad != 0) { if (upgrade_calendar_subscription_events_fix($info['subscription'], $output, $endtime)) { return true; // Not finished yet. } } // Let's fix the standard events (site, category, course, group). if ($info['standard']->bad != 0) { if (upgrade_calendar_standard_events_fix($info['standard'], $output, $endtime)) { return true; // Not finished yet. } } // Let's fix the action events (all them are "general" ones, not user-specific in core). if ($info['action']->bad != 0) { if (upgrade_calendar_action_events_fix($info['action'], $output, $endtime)) { return true; // Not finished yet. } } // Have arrived here, finished! return false; } /** * Wrapper over mtrace() to allow a few more things to be specified. * * @param string $string string to output. * @param bool $output true to perform the output, false to avoid it. */ function upgrade_calendar_events_mtrace(string $string, bool $output): void { static $cols = 0; // No output, do nothing. if (!$output) { return; } // Printing dots... let's output them slightly nicer. if ($string === '.') { $cols++; // Up to 60 cols. if ($cols < 60) { mtrace($string, ''); } else { mtrace($string); $cols = 0; } return; } // Reset cols, have ended printing dots. if ($cols) { $cols = 0; mtrace(''); } // Normal output. mtrace($string); } /** * Get a valid editing teacher for a given courseid * * @param int $courseid The course to look for editing teachers. * @return int A user id of an editing teacher or, if missing, the admin userid. */ function upgrade_calendar_events_get_teacherid(int $courseid): int { if ($context = context_course::instance($courseid, IGNORE_MISSING)) { if ($havemanage = get_users_by_capability($context, 'moodle/course:manageactivities', 'u.id')) { return array_keys($havemanage)[0]; } } return get_admin()->id; // Could not find a teacher, default to admin. } /** * Detects the calendar standard events needing to be fixed. With optional output. * * @param stdClass $info an object with total and bad counters, plus sql to retrieve them. * @param bool $output true if the function must output information, false if not. * @param int $endtime cutoff time when the process must stop (0 means no cutoff). * @return bool true if the function has not finished fixing everything, false if it has finished. */ function upgrade_calendar_standard_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool { global $DB; $return = false; // Let's assume the function is going to finish by default. $status = "Finished!"; // To decide the message to be presented on return. upgrade_calendar_events_mtrace('Processing standard events', $output); $rs = $DB->get_recordset_sql(" SELECT DISTINCT eventtype, courseid FROM {event} WHERE {$info->sql}"); foreach ($rs as $record) { switch ($record->eventtype) { case 'site': case 'category': // These are created by admin. $DB->set_field('event', 'userid', get_admin()->id, ['eventtype' => $record->eventtype]); break; case 'course': case 'group': // These are created by course teacher. $DB->set_field('event', 'userid', upgrade_calendar_events_get_teacherid($record->courseid), ['eventtype' => $record->eventtype, 'courseid' => $record->courseid]); break; } // Cutoff time, let's exit. if ($endtime && $endtime <= time()) { $status = 'Remaining standard events pending'; $return = true; // Not finished yet. break; } upgrade_calendar_events_mtrace('.', $output); } $rs->close(); upgrade_calendar_events_mtrace($status, $output); upgrade_calendar_events_mtrace('', $output); return $return; } /** * Detects the calendar subscription events needing to be fixed. With optional output. * * @param stdClass $info an object with total and bad counters, plus sql to retrieve them. * @param bool $output true if the function must output information, false if not. * @param int $endtime cutoff time when the process must stop (0 means no cutoff). * @return bool true if the function has not finished fixing everything, false if it has finished. */ function upgrade_calendar_subscription_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool { global $DB; $return = false; // Let's assume the function is going to finish by default. $status = "Finished!"; // To decide the message to be presented on return. upgrade_calendar_events_mtrace('Processing subscription events', $output); $rs = $DB->get_recordset_sql(" SELECT DISTINCT subscriptionid AS id FROM {event} WHERE {$info->sql}"); foreach ($rs as $subscription) { // Subscriptions can be site or category level, let's put the admin as userid. // (note that "user" subscription weren't deleted so there is nothing to recover with them. $DB->set_field('event_subscriptions', 'userid', get_admin()->id, ['id' => $subscription->id]); $DB->set_field('event', 'userid', get_admin()->id, ['subscriptionid' => $subscription->id]); // Cutoff time, let's exit. if ($endtime && $endtime <= time()) { $status = 'Remaining subscription events pending'; $return = true; // Not finished yet. break; } upgrade_calendar_events_mtrace('.', $output); } $rs->close(); upgrade_calendar_events_mtrace($status, $output); upgrade_calendar_events_mtrace('', $output); return $return; } /** * Detects the calendar action events needing to be fixed. With optional output. * * @param stdClass $info an object with total and bad counters, plus sql to retrieve them. * @param bool $output true if the function must output information, false if not. * @param int $endtime cutoff time when the process must stop (0 means no cutoff). * @return bool true if the function has not finished fixing everything, false if it has finished. */ function upgrade_calendar_action_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool { global $DB; $return = false; // Let's assume the function is going to finish by default. $status = "Finished!"; // To decide the message to be presented on return. upgrade_calendar_events_mtrace('Processing action events', $output); $rs = $DB->get_recordset_sql(" SELECT DISTINCT modulename, instance, courseid FROM {event} WHERE {$info->sql}"); foreach ($rs as $record) { // These are created by course teacher. $DB->set_field('event', 'userid', upgrade_calendar_events_get_teacherid($record->courseid), ['modulename' => $record->modulename, 'instance' => $record->instance, 'courseid' => $record->courseid]); // Cutoff time, let's exit. if ($endtime && $endtime <= time()) { $status = 'Remaining action events pending'; $return = true; // Not finished yet. break; } upgrade_calendar_events_mtrace('.', $output); } $rs->close(); upgrade_calendar_events_mtrace($status, $output); upgrade_calendar_events_mtrace('', $output); return $return; } /** * Detects the calendar override events needing to be fixed. With optional output. * * @param stdClass $info an object with total and bad counters, plus sql to retrieve them. * @param bool $output true if the function must output information, false if not. * @param int $endtime cutoff time when the process must stop (0 means no cutoff). * @return bool true if the function has not finished fixing everything, false if it has finished. */ function upgrade_calendar_override_events_fix(stdClass $info, bool $output = true, int $endtime = 0): bool { global $CFG, $DB; include_once($CFG->dirroot. '/course/lib.php'); include_once($CFG->dirroot. '/mod/assign/lib.php'); include_once($CFG->dirroot. '/mod/assign/locallib.php'); include_once($CFG->dirroot. '/mod/lesson/lib.php'); include_once($CFG->dirroot. '/mod/lesson/locallib.php'); include_once($CFG->dirroot. '/mod/quiz/lib.php'); include_once($CFG->dirroot. '/mod/quiz/locallib.php'); $return = false; // Let's assume the function is going to finish by default. $status = "Finished!"; // To decide the message to be presented on return. upgrade_calendar_events_mtrace('Processing override events', $output); $rs = $DB->get_recordset_sql(" SELECT DISTINCT modulename, instance FROM {event} WHERE {$info->sql}"); foreach ($rs as $module) { // Remove all the records from the events table for the module. $DB->delete_records('event', ['modulename' => $module->modulename, 'instance' => $module->instance]); // Get the activity record. if (!$activityrecord = $DB->get_record($module->modulename, ['id' => $module->instance])) { // Orphaned calendar event (activity doesn't exists), skip. continue; } // Let's rebuild it by calling to each module API. switch ($module->modulename) { case 'assign'; if (function_exists('assign_prepare_update_events')) { assign_prepare_update_events($activityrecord); } break; case 'lesson': if (function_exists('lesson_update_events')) { lesson_update_events($activityrecord); } break; case 'quiz': if (function_exists('quiz_update_events')) { quiz_update_events($activityrecord); } break; } // Sometimes, some (group) overrides are created without userid, when that happens, they deserve // some user (teacher or admin). This doesn't affect to groups calendar events behaviour, // but allows counters to detect already processed group overrides and makes things // consistent. $DB->set_field_select('event', 'userid', upgrade_calendar_events_get_teacherid($activityrecord->course), 'modulename = ? AND instance = ? and priority != 0 and userid = 0', ['modulename' => $module->modulename, 'instance' => $module->instance]); // Cutoff time, let's exit. if ($endtime && $endtime <= time()) { $status = 'Remaining override events pending'; $return = true; // Not finished yet. break; } upgrade_calendar_events_mtrace('.', $output); } $rs->close(); upgrade_calendar_events_mtrace($status, $output); upgrade_calendar_events_mtrace('', $output); return $return; } /** * Add a new item at the end of the usermenu. * * @param string $menuitem */ function upgrade_add_item_to_usermenu(string $menuitem): void { global $CFG; // Get current configuration data. $currentcustomusermenuitems = str_replace(["\r\n", "\r"], "\n", $CFG->customusermenuitems); $lines = preg_split('/\n/', $currentcustomusermenuitems, -1, PREG_SPLIT_NO_EMPTY); $lines = array_map('trim', $lines); if (!in_array($menuitem, $lines)) { // Add the item to the menu. $lines[] = $menuitem; set_config('customusermenuitems', implode("\n", $lines)); } } /** * Update all instances of a block shown on a pagetype to a new default region, adding missing block instances where * none is found. * * Note: This is intended as a helper to add blocks to all instances of the standard my-page. It will only work where * the subpagepattern is a string representation of an integer. If there are any string values this will not work. * * @param string $blockname The block name, without the block_ frankenstyle component * @param string $pagename The type of my-page to match * @param string $pagetypepattern The page type pattern to match for the block * @param string $newdefaultregion The new region to set */ function upgrade_block_set_defaultregion( string $blockname, string $pagename, string $pagetypepattern, string $newdefaultregion ): void { global $DB; // The subpagepattern is a string. // In all core blocks it contains a string represnetation of an integer, but it is theoretically possible for a // community block to do something different. // This function is not suited to those cases. $subpagepattern = $DB->sql_cast_char2int('bi.subpagepattern'); $subpageempty = $DB->sql_isnotempty('block_instances', 'bi.subpagepattern', true, false); // If a subquery returns any NULL then the NOT IN returns no results at all. // By adding a join in the inner select on my_pages we remove any possible nulls and prevent any need for // additional casting to filter out the nulls. $sql = <<execute($sql, [ 'selectblockname' => $blockname, 'contextuser' => CONTEXT_USER, 'selectpagetypepattern' => $pagetypepattern, 'selectdefaultregion' => $newdefaultregion, 'selecttimecreated' => time(), 'selecttimemodified' => time(), 'pagetypepattern' => $pagetypepattern, 'blockname' => $blockname, 'pagename' => $pagename, ]); // Update the existing instances. $sql = << :existingnewdefaultregion ) bid ) EOF; $DB->execute($sql, [ 'newdefaultregion' => $newdefaultregion, 'pagetypepattern' => $pagetypepattern, 'blockname' => $blockname, 'existingnewdefaultregion' => $newdefaultregion, 'pagename' => $pagename, ]); // Note: This can be time consuming! \context_helper::create_instances(CONTEXT_BLOCK); } /** * Remove all instances of a block on pages of the specified pagetypepattern. * * Note: This is intended as a helper to add blocks to all instances of the standard my-page. It will only work where * the subpagepattern is a string representation of an integer. If there are any string values this will not work. * * @param string $blockname The block name, without the block_ frankenstyle component * @param string $pagename The type of my-page to match * @param string $pagetypepattern This is typically used on the 'my-index' */ function upgrade_block_delete_instances( string $blockname, string $pagename, string $pagetypepattern ): void { global $DB; $deleteblockinstances = function (array $blockids) use ($DB) { [$insql, $inparams] = $DB->get_in_or_equal($blockids, SQL_PARAMS_NAMED, 'blockid'); $contextparams = array_merge(['contextlevel' => CONTEXT_BLOCK], $inparams); $DB->delete_records_select('context', "contextlevel = :contextlevel AND instanceid {$insql}", $contextparams); $DB->delete_records_select('block_positions', "blockinstanceid {$insql}", $inparams); $DB->delete_records_select('block_instances', "id {$insql}", $inparams); }; $deletepreferences = function (array $prefids) use ($DB) { [$insql, $params] = $DB->get_in_or_equal($prefids); $DB->delete_records_select('user_preferences', "id {$insql}", $params); }; $targetids = []; $batchsize = 1000; $collectids = function ($recordset) use (&$targetids) { foreach ($recordset as $record) { $targetids[(int)$record->id] = true; } $recordset->close(); }; $collectpreferences = function ($recordset, $targetids) { $prefstodelete = []; foreach ($recordset as $pref) { $blockid = (int)str_replace(['docked_block_instance_', 'block', 'hidden'], '', $pref->name); if (isset($targetids[$blockid])) { $prefstodelete[] = $pref->id; } } $recordset->close(); return $prefstodelete; }; // Collect block IDs from the default indexsys version of the block. $subpagepattern = $DB->get_record('my_pages', [ 'userid' => null, 'name' => $pagename, 'private' => MY_PAGE_PRIVATE, ], 'id', IGNORE_MULTIPLE)->id; $recordset = $DB->get_recordset('block_instances', [ 'blockname' => $blockname, 'pagetypepattern' => $pagetypepattern, 'subpagepattern' => $subpagepattern, ], '', 'id'); $collectids($recordset); // The subpagepattern is a string. // In all core blocks it contains a string represnetation of an integer, but it is theoretically possible for a // community block to do something different. // This function is not suited to those cases. $subpagepattern = $DB->sql_cast_char2int('bi.subpagepattern'); // Look for any and all instances of the block in customised /my pages. $subpageempty = $DB->sql_isnotempty('block_instances', 'bi.subpagepattern', true, false); $customblockssql = <<get_recordset_sql($customblockssql, [ 'blockname' => $blockname, 'pagetypepattern' => $pagetypepattern, 'pagename' => $pagename, 'private' => MY_PAGE_PRIVATE, ]); $collectids($recordset); if (empty($targetids)) { return; } // Collect preference IDs associated with these blocks. $dockedlike = $DB->sql_like('name', ':docked', false); $hiddenlike = $DB->sql_like('name', ':hidden', false); $prefssql = <<get_recordset_sql($prefssql, [ 'docked' => 'docked_block_instance_%', 'hidden' => 'block%hidden', ]); $prefstodelete = $collectpreferences($recordset, $targetids); // Delete preferences in batches. foreach (array_chunk($prefstodelete, $batchsize) as $batch) { $deletepreferences($batch); } // Delete block instances and related records in batches. foreach (array_chunk(array_keys($targetids), $batchsize) as $batch) { $deleteblockinstances($batch); } } /** * Update the block instance parentcontext to point to the correct user context id for the specified block on a my page. * * @param string $blockname * @param string $pagename * @param string $pagetypepattern */ function upgrade_block_set_my_user_parent_context( string $blockname, string $pagename, string $pagetypepattern ): void { global $DB; $subpagepattern = $DB->sql_cast_char2int('bi.subpagepattern'); // Look for any and all instances of the block in customised /my pages. $subpageempty = $DB->sql_isnotempty('block_instances', 'bi.subpagepattern', true, false); $dbman = $DB->get_manager(); $temptablename = 'block_instance_context'; $xmldbtable = new \xmldb_table($temptablename); $xmldbtable->add_field('instanceid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null); $xmldbtable->add_field('contextid', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, null); $xmldbtable->add_key('primary', XMLDB_KEY_PRIMARY, ['instanceid']); $dbman->create_temp_table($xmldbtable); $sql = << c.id EOF; $DB->execute($sql, [ 'blockname' => $blockname, 'pagetypepattern' => $pagetypepattern, 'contextuser' => CONTEXT_USER, 'pagename' => $pagename, ]); $dbfamily = $DB->get_dbfamily(); if ($dbfamily === 'mysql') { // MariaDB and MySQL. $sql = <<execute($sql); $dbman->drop_table($xmldbtable); } /** * Fix the timestamps for files where their timestamps are older * than the directory listing that they are contained in. */ function upgrade_fix_file_timestamps() { global $DB; // Due to incompatability in SQL syntax for updates with joins, // These will be updated in a select + separate update. $sql = "SELECT f.id, f2.timecreated FROM {files} f JOIN {files} f2 ON f2.contextid = f.contextid AND f2.filepath = f.filepath AND f2.component = f.component AND f2.filearea = f.filearea AND f2.itemid = f.itemid AND f2.filename = '.' WHERE f2.timecreated > f.timecreated"; $recordset = $DB->get_recordset_sql($sql); if (!$recordset->valid()) { $recordset->close(); return; } foreach ($recordset as $record) { $record->timemodified = $record->timecreated; $DB->update_record('files', $record); } $recordset->close(); } /** * Upgrade helper to add foreign keys and indexes for MDL-49795 */ function upgrade_add_foreign_key_and_indexes() { global $DB; $dbman = $DB->get_manager(); // Define key originalcourseid (foreign) to be added to course. $table = new xmldb_table('course'); $key = new xmldb_key('originalcourseid', XMLDB_KEY_FOREIGN, ['originalcourseid'], 'course', ['id']); // Launch add key originalcourseid. $dbman->add_key($table, $key); // Define key roleid (foreign) to be added to enrol. $table = new xmldb_table('enrol'); $key = new xmldb_key('roleid', XMLDB_KEY_FOREIGN, ['roleid'], 'role', ['id']); // Launch add key roleid. $dbman->add_key($table, $key); // Define key userid (foreign) to be added to scale. $table = new xmldb_table('scale'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key userid (foreign) to be added to scale_history. $table = new xmldb_table('scale_history'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key courseid (foreign) to be added to post. $table = new xmldb_table('post'); $key = new xmldb_key('courseid', XMLDB_KEY_FOREIGN, ['courseid'], 'course', ['id']); // Launch add key courseid. $dbman->add_key($table, $key); // Define key coursemoduleid (foreign) to be added to post. $table = new xmldb_table('post'); $key = new xmldb_key('coursemoduleid', XMLDB_KEY_FOREIGN, ['coursemoduleid'], 'course_modules', ['id']); // Launch add key coursemoduleid. $dbman->add_key($table, $key); // Define key questionid (foreign) to be added to question_statistics. $table = new xmldb_table('question_statistics'); $key = new xmldb_key('questionid', XMLDB_KEY_FOREIGN, ['questionid'], 'question', ['id']); // Launch add key questionid. $dbman->add_key($table, $key); // Define key questionid (foreign) to be added to question_response_analysis. $table = new xmldb_table('question_response_analysis'); $key = new xmldb_key('questionid', XMLDB_KEY_FOREIGN, ['questionid'], 'question', ['id']); // Launch add key questionid. $dbman->add_key($table, $key); // Define index last_log_id (not unique) to be added to mnet_host. $table = new xmldb_table('mnet_host'); $index = new xmldb_index('last_log_id', XMLDB_INDEX_NOTUNIQUE, ['last_log_id']); // Conditionally launch add index last_log_id. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Define key userid (foreign) to be added to mnet_session. $table = new xmldb_table('mnet_session'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key mnethostid (foreign) to be added to mnet_session. $table = new xmldb_table('mnet_session'); $key = new xmldb_key('mnethostid', XMLDB_KEY_FOREIGN, ['mnethostid'], 'mnet_host', ['id']); // Launch add key mnethostid. $dbman->add_key($table, $key); // Define key userid (foreign) to be added to grade_import_values. $table = new xmldb_table('grade_import_values'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key tempdataid (foreign) to be added to portfolio_log. $table = new xmldb_table('portfolio_log'); $key = new xmldb_key('tempdataid', XMLDB_KEY_FOREIGN, ['tempdataid'], 'portfolio_tempdata', ['id']); // Launch add key tempdataid. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to file_conversion. $table = new xmldb_table('file_conversion'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key userid (foreign) to be added to repository_instances. $table = new xmldb_table('repository_instances'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key contextid (foreign) to be added to repository_instances. $table = new xmldb_table('repository_instances'); $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']); // Launch add key contextid. $dbman->add_key($table, $key); // Define key scaleid (foreign) to be added to rating. $table = new xmldb_table('rating'); $key = new xmldb_key('scaleid', XMLDB_KEY_FOREIGN, ['scaleid'], 'scale', ['id']); // Launch add key scaleid. $dbman->add_key($table, $key); // Define key courseid (foreign) to be added to course_published. $table = new xmldb_table('course_published'); $key = new xmldb_key('courseid', XMLDB_KEY_FOREIGN, ['courseid'], 'course', ['id']); // Launch add key courseid. $dbman->add_key($table, $key); // Define index hubcourseid (not unique) to be added to course_published. $table = new xmldb_table('course_published'); $index = new xmldb_index('hubcourseid', XMLDB_INDEX_NOTUNIQUE, ['hubcourseid']); // Conditionally launch add index hubcourseid. if (!$dbman->index_exists($table, $index)) { $dbman->add_index($table, $index); } // Define key courseid (foreign) to be added to event_subscriptions. $table = new xmldb_table('event_subscriptions'); $key = new xmldb_key('courseid', XMLDB_KEY_FOREIGN, ['courseid'], 'course', ['id']); // Launch add key courseid. $dbman->add_key($table, $key); // Define key userid (foreign) to be added to event_subscriptions. $table = new xmldb_table('event_subscriptions'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key userid (foreign) to be added to task_log. $table = new xmldb_table('task_log'); $key = new xmldb_key('userid', XMLDB_KEY_FOREIGN, ['userid'], 'user', ['id']); // Launch add key userid. $dbman->add_key($table, $key); // Define key scaleid (foreign) to be added to competency. $table = new xmldb_table('competency'); $key = new xmldb_key('scaleid', XMLDB_KEY_FOREIGN, ['scaleid'], 'scale', ['id']); // Launch add key scaleid. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency. $table = new xmldb_table('competency'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_coursecompsetting. $table = new xmldb_table('competency_coursecompsetting'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key contextid (foreign) to be added to competency_framework. $table = new xmldb_table('competency_framework'); $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']); // Launch add key contextid. $dbman->add_key($table, $key); // Define key scaleid (foreign) to be added to competency_framework. $table = new xmldb_table('competency_framework'); $key = new xmldb_key('scaleid', XMLDB_KEY_FOREIGN, ['scaleid'], 'scale', ['id']); // Launch add key scaleid. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_framework. $table = new xmldb_table('competency_framework'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_coursecomp. $table = new xmldb_table('competency_coursecomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key actionuserid (foreign) to be added to competency_evidence. $table = new xmldb_table('competency_evidence'); $key = new xmldb_key('actionuserid', XMLDB_KEY_FOREIGN, ['actionuserid'], 'user', ['id']); // Launch add key actionuserid. $dbman->add_key($table, $key); // Define key contextid (foreign) to be added to competency_evidence. $table = new xmldb_table('competency_evidence'); $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']); // Launch add key contextid. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_evidence. $table = new xmldb_table('competency_evidence'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_userevidence. $table = new xmldb_table('competency_userevidence'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_plan. $table = new xmldb_table('competency_plan'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_template. $table = new xmldb_table('competency_template'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key contextid (foreign) to be added to competency_template. $table = new xmldb_table('competency_template'); $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']); // Launch add key contextid. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_templatecomp. $table = new xmldb_table('competency_templatecomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_templatecohort. $table = new xmldb_table('competency_templatecohort'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key competencyid (foreign) to be added to competency_relatedcomp. $table = new xmldb_table('competency_relatedcomp'); $key = new xmldb_key('competencyid', XMLDB_KEY_FOREIGN, ['competencyid'], 'competency', ['id']); // Launch add key competencyid. $dbman->add_key($table, $key); // Define key relatedcompetencyid (foreign) to be added to competency_relatedcomp. $table = new xmldb_table('competency_relatedcomp'); $key = new xmldb_key('relatedcompetencyid', XMLDB_KEY_FOREIGN, ['relatedcompetencyid'], 'competency', ['id']); // Launch add key relatedcompetencyid. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_relatedcomp. $table = new xmldb_table('competency_relatedcomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_usercomp. $table = new xmldb_table('competency_usercomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_usercompcourse. $table = new xmldb_table('competency_usercompcourse'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_usercompplan. $table = new xmldb_table('competency_usercompplan'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_plancomp. $table = new xmldb_table('competency_plancomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_userevidencecomp. $table = new xmldb_table('competency_userevidencecomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to competency_modulecomp. $table = new xmldb_table('competency_modulecomp'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to oauth2_endpoint. $table = new xmldb_table('oauth2_endpoint'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to oauth2_system_account. $table = new xmldb_table('oauth2_system_account'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to oauth2_user_field_mapping. $table = new xmldb_table('oauth2_user_field_mapping'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to analytics_models. $table = new xmldb_table('analytics_models'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to analytics_models_log. $table = new xmldb_table('analytics_models_log'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key usermodified (foreign) to be added to oauth2_access_token. $table = new xmldb_table('oauth2_access_token'); $key = new xmldb_key('usermodified', XMLDB_KEY_FOREIGN, ['usermodified'], 'user', ['id']); // Launch add key usermodified. $dbman->add_key($table, $key); // Define key contextid (foreign) to be added to payment_accounts. $table = new xmldb_table('payment_accounts'); $key = new xmldb_key('contextid', XMLDB_KEY_FOREIGN, ['contextid'], 'context', ['id']); // Launch add key contextid. $dbman->add_key($table, $key); } /** * Upgrade helper to change a binary column to an integer column with a length of 1 in a consistent manner across databases. * * This function will * - rename the existing column to a temporary name, * - add a new column with the integer type, * - copy the values from the old column to the new column, * - and finally, drop the old column. * * This function will do nothing if the field is already an integer. * * The new column with the integer type will need to have a default value of 0. * This is to avoid breaking the not null constraint, if it's set, especially if there are existing records. * Please make sure that the column definition in install.xml also has the `DEFAULT` attribute value set to 0. * * @param string $tablename The name of the table. * @param string $fieldname The name of the field to be converted. * @param bool|null $notnull {@see XMLDB_NOTNULL} or null. * @param string|null $previous The name of the field that this field should come after. * @return bool */ function upgrade_change_binary_column_to_int( string $tablename, string $fieldname, ?bool $notnull = null, ?string $previous = null, ): bool { global $DB; // Get the information about the field to be converted. $columns = $DB->get_columns($tablename); $toconvert = $columns[$fieldname]; // Check if the field to be converted is already an integer-type column (`meta_type` property of 'I'). if ($toconvert->meta_type === 'I') { // Nothing to do if the field is already an integer-type. return false; } else if (!$toconvert->binary) { throw new \core\exception\coding_exception( 'This function is only used to convert XMLDB_TYPE_BINARY fields to XMLDB_TYPE_INTEGER fields. ' . 'For other field types, please check out \database_manager::change_field_type()' ); } $dbman = $DB->get_manager(); $table = new xmldb_table($tablename); // Temporary rename the field. We'll drop this later. $tmpfieldname = "tmp$fieldname"; $field = new xmldb_field($fieldname, XMLDB_TYPE_BINARY); $dbman->rename_field($table, $field, $tmpfieldname); // Add the new field wih the integer type. $field = new xmldb_field($fieldname, XMLDB_TYPE_INTEGER, '1', null, $notnull, null, '0', $previous); $dbman->add_field($table, $field); // Copy the 'true' values from the old field to the new field. $sql = 'UPDATE {' . $tablename . '} SET ' . $fieldname . ' = 1 WHERE ' . $tmpfieldname . ' = ?'; $DB->execute($sql, [1]); // Drop the old field. $oldfield = new xmldb_field($tmpfieldname); $dbman->drop_field($table, $oldfield); return true; } /** * Upgrade script replacing absolute URLs in defaulthomepage setting with relative URLs */ function upgrade_store_relative_url_sitehomepage() { global $CFG, $DB; if (str_starts_with((string)$CFG->defaulthomepage, $CFG->wwwroot . '/')) { set_config('defaulthomepage', substr((string)$CFG->defaulthomepage, strlen($CFG->wwwroot))); } $records = $DB->get_records_select('user_preferences', "name = :name AND " . $DB->sql_like('value', ':pattern'), ['name' => 'user_home_page_preference', 'pattern' => 'http%']); foreach ($records as $record) { if (str_starts_with($record->value, $CFG->wwwroot . '/')) { $DB->update_record('user_preferences', [ 'id' => $record->id, 'value' => substr($record->value, strlen($CFG->wwwroot)), ]); } } } /** * Upgrade script to convert existing AI providers to provider instances. */ function upgrade_convert_ai_providers_to_instances() { global $DB; // Start with the azureai provider. // Only migrate the provider if it is enabled. $azureaiconfig = get_config('aiprovider_azureai'); if (!empty($azureaiconfig->enabled) || !empty($azureaiconfig->apikey)) { // Create the instance config. We don't want everything from the provider config. $instanceconfig = [ 'aiprovider' => \aiprovider_azureai\provider::class, 'name' => get_string('pluginname', 'aiprovider_azureai'), 'apikey' => $azureaiconfig->apikey ?? '', 'endpoint' => $azureaiconfig->endpoint ?? '', 'enableglobalratelimit' => $azureaiconfig->enableglobalratelimit ?? 0, 'globalratelimit' => $azureaiconfig->globalratelimit ?? 100, 'enableuserratelimit' => $azureaiconfig->enableuserratelimit ?? 0, 'userratelimit' => $azureaiconfig->userratelimit ?? 10, ]; $actionconfig = [ 'core_ai\aiactions\generate_text' => [ 'enabled' => $azureaiconfig->generate_text ?? true, 'settings' => [ 'deployment' => $azureaiconfig->action_generate_text_deployment ?? '', 'apiversion' => $azureaiconfig->action_generate_text_apiversion ?? '2024-06-01', 'systeminstruction' => $azureaiconfig->action_generate_text_systeminstruction ?? get_string('action_generate_text_instruction', 'core_ai'), ], ], 'core_ai\aiactions\generate_image' => [ 'enabled' => $azureaiconfig->generate_image ?? true, 'settings' => [ 'deployment' => $azureaiconfig->action_generate_image_deployment ?? '', 'apiversion' => $azureaiconfig->action_generate_image_apiversion ?? '2024-06-01', ], ], 'core_ai\aiactions\summarise_text' => [ 'enabled' => $azureaiconfig->summarise_text ?? true, 'settings' => [ 'deployment' => $azureaiconfig->action_summarise_text_deployment ?? '', 'apiversion' => $azureaiconfig->action_summarise_text_apiversion ?? '2024-06-01', 'systeminstruction' => $azureaiconfig->action_generate_text_systeminstruction ?? get_string('action_summarise_text_instruction', 'core_ai'), ], ], ]; // Because of the upgrade code restrictions we insert directly into the database and don't use the AI manager class. $record = new stdClass(); $record->name = get_string('pluginname', 'aiprovider_azureai'); $record->provider = \aiprovider_azureai\provider::class; $record->enabled = $azureaiconfig->enabled ?? false; $record->config = json_encode($instanceconfig); $record->actionconfig = json_encode($actionconfig); $DB->insert_record('ai_providers', $record); } // Now do the same for the openai provider. $openaiconfig = get_config('aiprovider_openai'); if (!empty($openaiconfig->enabled) || !empty($openaiconfig->apikey)) { // Create the instance config. We don't want everything from the provider config. $instanceconfig = [ 'aiprovider' => \aiprovider_openai\provider::class, 'name' => get_string('pluginname', 'aiprovider_openai'), 'apikey' => $openaiconfig->apikey ?? '', 'orgid' => $openaiconfig->orgid ?? '', 'enableglobalratelimit' => $openaiconfig->enableglobalratelimit ?? 0, 'globalratelimit' => $openaiconfig->globalratelimit ?? 100, 'enableuserratelimit' => $openaiconfig->enableuserratelimit ?? 0, 'userratelimit' => $openaiconfig->userratelimit ?? 10, ]; $actionconfig = [ 'core_ai\aiactions\generate_text' => [ 'enabled' => $openaiconfig->generate_text ?? true, 'settings' => [ 'model' => $openaiconfig->action_generate_text_model ?? 'gpt-4o', 'endpoint' => $openaiconfig->action_generate_text_endpoint ?? 'https://api.openai.com/v1/chat/completions', 'systeminstruction' => $openaiconfig->action_generate_text_systeminstruction ?? get_string('action_generate_text_instruction', 'core_ai'), ], ], 'core_ai\aiactions\generate_image' => [ 'enabled' => $openaiconfig->generate_image ?? true, 'settings' => [ 'model' => $openaiconfig->action_generate_text_model ?? 'dall-e-3', 'endpoint' => $openaiconfig->action_generate_text_endpoint ?? 'https://api.openai.com/v1/images/generations', ], ], 'core_ai\aiactions\summarise_text' => [ 'enabled' => $openaiconfig->summarise_text ?? true, 'settings' => [ 'model' => $openaiconfig->action_generate_text_model ?? 'gpt-4o', 'endpoint' => $openaiconfig->action_generate_text_endpoint ?? 'https://api.openai.com/v1/chat/completions', 'systeminstruction' => $openaiconfig->action_generate_text_systeminstruction ?? get_string('action_summarise_text_instruction', 'core_ai'), ], ], ]; $record = new stdClass(); $record->name = get_string('pluginname', 'aiprovider_openai'); $record->provider = \aiprovider_openai\provider::class; $record->enabled = $openaiconfig->enabled ?? false; $record->config = json_encode($instanceconfig); $record->actionconfig = json_encode($actionconfig); $DB->insert_record('ai_providers', $record); } // Finally remove the config settings from the plugin config table. $azuresettings = ['enabled', 'apikey', 'endpoint', 'enableglobalratelimit', 'globalratelimit', 'enableuserratelimit', 'userratelimit', 'generate_text', 'action_generate_text_enabled', 'action_generate_text_deployment', 'action_generate_text_apiversion', 'action_generate_text_systeminstruction', 'generate_image', 'action_generate_image_enabled', 'action_generate_image_deployment', 'action_generate_image_apiversion', 'summarise_text', 'action_summarise_text_enabled', 'action_summarise_text_deployment', 'action_summarise_text_apiversion', 'action_summarise_text_systeminstruction']; array_walk($azuresettings, static fn($setting) => unset_config($setting, 'aiprovider_azureai')); $openaisettings = ['enabled', 'apikey', 'orgid', 'enableglobalratelimit', 'globalratelimit', 'enableuserratelimit', 'userratelimit', 'generate_text', 'action_generate_text_enabled', 'action_generate_text_model', 'action_generate_text_endpoint', 'action_generate_text_systeminstruction', 'generate_image', 'action_generate_image_enabled', 'action_generate_image_model', 'action_generate_image_endpoint', 'summarise_text', 'action_summarise_text_enabled', 'action_summarise_text_model', 'action_summarise_text_endpoint', 'action_summarise_text_systeminstruction']; array_walk($openaisettings, static fn($setting) => unset_config($setting, 'aiprovider_openai')); } /** * Upgrade script to get all current AI providers and update their action config to include explain. */ function upgrade_add_explain_action_to_ai_providers() { global $DB; $currentrecords = $DB->get_recordset('ai_providers'); foreach ($currentrecords as $currentrecord) { $actionconfig = json_decode($currentrecord->actionconfig, true); if ($currentrecord->provider === 'aiprovider_openai\provider') { $explainconfig = [ 'enabled' => true, 'settings' => [ 'model' => 'gpt-4o', 'endpoint' => 'https://api.openai.com/v1/chat/completions', 'systeminstruction' => get_string('action_explain_text_instruction', 'core_ai'), ], ]; } else if ($currentrecord->provider === 'aiprovider_azureai\provider') { $explainconfig = [ 'enabled' => true, 'settings' => [ 'deployment' => '', 'apiversion' => '2024-06-01', 'systeminstruction' => get_string('action_explain_text_instruction', 'core_ai'), ], ]; } // Update the record with the changes. if (!empty($explainconfig)) { $actionconfig['core_ai\aiactions\explain_text'] = $explainconfig; $currentrecord->actionconfig = json_encode($actionconfig); $DB->update_record('ai_providers', $currentrecord); } } $currentrecords->close(); } /** * Creates a new ad-hoc task to upgrade the mime-type of files asynchronously. * Thus, we can considerably reduce the time an upgrade takes. * * @param string $mimetype the desired mime-type * @param string[] $extensions a list of file extensions, without the leading dot * @return void */ function upgrade_create_async_mimetype_upgrade_task(string $mimetype, array $extensions): void { global $DB; // Create adhoc task for upgrading of existing files. Due to a code restriction on the upgrade, invoking any core // functions is not permitted. Thus we craft our own ad-hoc task that will process all existing files. $record = new \stdClass(); $record->classname = '\core_files\task\asynchronous_mimetype_upgrade_task'; $record->component = 'core'; $record->customdata = json_encode([ 'mimetype' => $mimetype, 'extensions' => $extensions, ]); // Next run time based from nextruntime computation in \core\task\manager::queue_adhoc_task(). $clock = \core\di::get(\core\clock::class); $nextruntime = $clock->time() - 1; $record->nextruntime = $nextruntime; $DB->insert_record('task_adhoc', $record); } hooks.php000064400000013757152266565400006430 0ustar00. /** * Hook listener callbacks. * * @package core * @copyright 2023 Safat Shahin * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $callbacks = [ [ 'hook' => \core_group\hook\after_group_created::class, 'callback' => \core_communication\hook_listener::class . '::create_group_communication', ], [ 'hook' => \core_group\hook\after_group_updated::class, 'callback' => \core_communication\hook_listener::class . '::update_group_communication', ], [ 'hook' => \core_group\hook\after_group_deleted::class, 'callback' => \core_communication\hook_listener::class . '::delete_group_communication', ], [ 'hook' => \core_group\hook\after_group_membership_added::class, 'callback' => \core_communication\hook_listener::class . '::add_members_to_group_room', ], [ 'hook' => \core_group\hook\after_group_membership_removed::class, 'callback' => \core_communication\hook_listener::class . '::remove_members_from_group_room', ], [ 'hook' => \core_group\hook\after_group_membership_added::class, 'callback' => \core_courseformat\hook_listener::class . '::add_members_to_group', ], [ 'hook' => \core_group\hook\after_group_membership_removed::class, 'callback' => \core_courseformat\hook_listener::class . '::remove_members_from_group', ], [ 'hook' => \core_courseformat\hook\after_course_content_updated::class, 'callback' => \core_courseformat\hook_listener::class . '::course_content_updated', ], [ 'hook' => core\hook\access\after_role_switched::class, 'callback' => \core_courseformat\hook_listener::class . '::after_role_switched', ], [ 'hook' => \core_completion\hook\after_cm_completion_updated::class, 'callback' => \core_courseformat\hook_listener::class . '::after_cm_completion_updated', ], [ 'hook' => \core_course\hook\after_course_created::class, 'callback' => \core_communication\hook_listener::class . '::create_course_communication', ], [ 'hook' => \core_course\hook\after_course_updated::class, 'callback' => \core_communication\hook_listener::class . '::update_course_communication', ], [ 'hook' => \core_course\hook\before_course_deleted::class, 'callback' => \core_communication\hook_listener::class . '::delete_course_communication', ], [ 'hook' => \core_user\hook\before_user_updated::class, 'callback' => \core_communication\hook_listener::class . '::update_user_room_memberships', ], [ 'hook' => \core_user\hook\before_user_deleted::class, 'callback' => \core_communication\hook_listener::class . '::delete_user_room_memberships', ], [ 'hook' => \core\hook\access\after_role_assigned::class, 'callback' => \core_communication\hook_listener::class . '::update_user_membership_for_role_changes', ], [ 'hook' => \core\hook\access\after_role_unassigned::class, 'callback' => \core_communication\hook_listener::class . '::update_user_membership_for_role_changes', ], [ 'hook' => \core_enrol\hook\after_enrol_instance_status_updated::class, 'callback' => \core_communication\hook_listener::class . '::update_communication_memberships_for_enrol_status_change', ], [ 'hook' => \core_enrol\hook\before_enrol_instance_deleted::class, 'callback' => \core_communication\hook_listener::class . '::remove_communication_memberships_for_enrol_instance_deletion', ], [ 'hook' => \core_enrol\hook\after_user_enrolled::class, 'callback' => \core_communication\hook_listener::class . '::add_communication_membership_for_enrolled_user', ], [ 'hook' => \core_enrol\hook\before_user_enrolment_updated::class, 'callback' => \core_communication\hook_listener::class . '::update_communication_membership_for_updated_user_enrolment', ], [ 'hook' => \core_enrol\hook\before_user_enrolment_removed::class, 'callback' => \core_communication\hook_listener::class . '::remove_communication_membership_for_unenrolled_user', ], [ 'hook' => \core\hook\output\before_standard_footer_html_generation::class, 'callback' => \core_userfeedback::class . '::before_standard_footer_html_generation', ], [ 'hook' => \core\hook\output\after_standard_main_region_html_generation::class, 'callback' => \core_message\hook_callbacks::class . '::add_messaging_widget', 'priority' => 0, ], [ 'hook' => \core\hook\task\after_failed_task_max_delay::class, 'callback' => core\task\failed_task_callbacks::class . '::send_failed_task_max_delay_message', ], [ 'hook' => \core\hook\di_configuration::class, 'callback' => [\core\router\hook_callbacks::class, 'provide_di_configuration'], ], [ 'hook' => \core_files\hook\before_file_created::class, 'callback' => [\core_files\redactor\hook_listener::class, 'file_redaction_handler'], ], [ 'hook' => \core_course\hook\before_course_viewed::class, 'callback' => [\core_courseformat\hook_listener::class, 'before_course_viewed'], 'priority' => 999, ], ]; messages.php000064400000020452152266565400007102 0ustar00. /** * Defines message providers (types of messages being sent) * * The providers defined on this file are processed and registered into * the Moodle DB after any install or upgrade operation. All plugins * support this. * * For more information, take a look to the documentation available: * - Message API: {@link http://docs.moodle.org/dev/Message_API} * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @package core * @category message * @copyright 2008 onwards Martin Dougiamas http://dougiamas.com * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $messageproviders = array ( 'newlogin' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ), // Notices that an admin might be interested in. 'notices' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:config', ), // Important errors that an admin ought to know about. 'errors' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:config', ), // Cron-based notifications about available moodle and/or additional plugin updates. 'availableupdate' => array( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:config', ), 'instantmessage' => array ( 'defaults' => array( 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ), ), 'backup' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:config' ), // Course creation request notification 'courserequested' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:approvecourse' ), // Course request approval notification. 'courserequestapproved' => array ( 'capability' => 'moodle/course:request', 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ), // Course request rejection notification. 'courserequestrejected' => array ( 'capability' => 'moodle/course:request', 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ), // Course completed. Requires course completion configured at course level. It does not work with just activity progress. 'coursecompleted' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ], // Course content updated. New content (activities or resources) has been created or existing content updated. 'coursecontentupdated' => array ( 'defaults' => array( 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ), ), // Badge award notification to a badge recipient. 'badgerecipientnotice' => array ( 'defaults' => array( 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ), 'capability' => 'moodle/badges:earnbadge' ), // Badge award notification to a badge creator (mostly cron-based). 'badgecreatornotice' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ), // A comment was left on a plan. 'competencyplancomment' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ], // A comment was left on a user competency. 'competencyusercompcomment' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ], // User insights. 'insights' => array ( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ] ), // Message contact requests. 'messagecontactrequests' => [ 'defaults' => [ // We don't need to notify in the popup output here because the message drawer // already notifies users of contact requests. 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ] ], // Asyncronhous backup/restore notifications. 'asyncbackupnotification' => array( 'defaults' => array( 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ) ), 'gradenotifications' => [ 'defaults' => array( 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ), ], // Infected files. 'infected' => array( 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:config', ), // Report builder schedules. 'reportbuilderschedule' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_FORCED, ], ], // Task has reached the maximum fail delay. 'failedtaskmaxdelay' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], 'capability' => 'moodle/site:config', ], // Course welcome message. 'enrolcoursewelcomemessage' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'airnotifier' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ], ); log.php000064400000010704152267011150006040 0ustar00. /** * Definition of log events associated with the current component * * The log events defined on this file are processed and stored into * the Moodle DB after any install or upgrade operation. All plugins * support this. * * For more information, take a look to the documentation available: * - Upgrade API: {@link https://moodledev.io/docs/guides/upgrade} * * @package core * @category log * @copyright 2010 Petr Skoda (http://skodak.org) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); global $DB; // TODO: this is a hack, we should really do something with the SQL in SQL tables $logs = array( array('module'=>'course', 'action'=>'user report', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'course', 'action'=>'view', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'view section', 'mtable'=>'course_sections', 'field'=>'name'), array('module'=>'course', 'action'=>'update', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'hide', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'show', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'move', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'enrol', 'mtable'=>'course', 'field'=>'fullname'), // there should be some way to store user id of the enrolled user! array('module'=>'course', 'action'=>'unenrol', 'mtable'=>'course', 'field'=>'fullname'), // there should be some way to store user id of the enrolled user! array('module'=>'course', 'action'=>'report log', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'report live', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'report outline', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'report participation', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'course', 'action'=>'report stats', 'mtable'=>'course', 'field'=>'fullname'), array('module'=>'category', 'action'=>'add', 'mtable'=>'course_categories', 'field'=>'name'), array('module'=>'category', 'action'=>'hide', 'mtable'=>'course_categories', 'field'=>'name'), array('module'=>'category', 'action'=>'move', 'mtable'=>'course_categories', 'field'=>'name'), array('module'=>'category', 'action'=>'show', 'mtable'=>'course_categories', 'field'=>'name'), array('module'=>'category', 'action'=>'update', 'mtable'=>'course_categories', 'field'=>'name'), array('module'=>'message', 'action'=>'write', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'message', 'action'=>'read', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'message', 'action'=>'add contact', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'message', 'action'=>'remove contact', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'message', 'action'=>'block contact', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'message', 'action'=>'unblock contact', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), array('module'=>'group', 'action'=>'view', 'mtable'=>'groups', 'field'=>'name'), array('module'=>'tag', 'action'=>'update', 'mtable'=>'tag', 'field'=>'name'), array('module'=>'tag', 'action'=>'flag', 'mtable'=>'tag', 'field'=>'name'), array('module'=>'user', 'action'=>'view', 'mtable'=>'user', 'field'=>$DB->sql_concat('firstname', "' '" , 'lastname')), ); subplugins.json000064400000000210152267012770007634 0ustar00{ "subplugintypes": { "forumreport": "report" }, "plugintypes": { "forumreport": "mod/forum/report" } } mobile.php000064400000002631152267015350006534 0ustar00. /** * Structure to support subsection in versions of the app previous to 4.5. * * @package mod_subsection * @copyright 2024 Dani Palou * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $addons = [ 'mod_subsection' => [ 'handlers' => [ 'subsection' => [ 'delegate' => '', // Don't use a delegate, the JS code will register the handler. 'init' => 'mobile_init', 'styles' => [ 'url' => $CFG->wwwroot . '/mod/subsection/mobileapp/styles.css', 'version' => '1', ], ], ], ], ]; uninstall.php000064400000002505152267015350007276 0ustar00. /** * This file replaces the legacy STATEMENTS section in db/install.xml, * lib.php/modulename_install() post installation hook and partially defaults.php * * @package mod_workshop * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * This is called at the beginning of the uninstallation process to give the module * a chance to clean-up its hacks, bits etc. where possible. * * @return bool true if success */ function mod_workshop_uninstall() { // global $DB; // $dbman = $DB->get_manager(); return true; } tag.php000064400000007760152267074040006051 0ustar00. /** * Tag area definitions * * File db/tag.php lists all available tag areas in core or a plugin. * * Each tag area may have the following attributes: * - itemtype (required) - what is tagged. Must be name of the existing DB table * - component - component responsible for tagging, if the tag area is inside a * plugin the component must be the full frankenstyle name of the plugin * - collection - name of the custom tag collection that will be used to store * tags in this area. If specified aministrator will be able to neither add * any other tag areas to this collection nor move this tag area elsewhere * - searchable (only if collection is specified) - wether the tag collection * should be searchable on /tag/search.php * - showstandard - default value for the "Standard tags" attribute of the area, * this is only respected when new tag area is added and ignored during upgrade * - customurl (only if collection is specified) - custom url to use instead of * /tag/search.php to display information about one tag * - callback - name of the function that returns items tagged with this tag, * see core_tag_tag::get_tag_index() and existing callbacks for more details, * callback should return instance of core_tag\output\tagindex * - callbackfile - file where callback is located (if not an autoloaded location) * * Language file must contain the human-readable names of the tag areas and * collections (either in plugin language file or in component language file or * lang/en/tag.php in case of core): * - for item type "user": * $string['tagarea_user'] = 'Users'; * - for tag collection "mycollection": * $string['tagcollection_mycollection'] = 'My tag collection'; * * @package core * @copyright 2015 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $tagareas = [ [ 'itemtype' => 'user', // Users. 'component' => 'core', 'callback' => 'user_get_tagged_users', 'callbackfile' => '/user/lib.php', 'showstandard' => core_tag_tag::HIDE_STANDARD, ], [ 'itemtype' => 'course', // Courses. 'component' => 'core', 'callback' => 'course_get_tagged_courses', 'callbackfile' => '/course/lib.php', ], [ 'itemtype' => 'question', // Questions. 'component' => 'core_question', 'multiplecontexts' => true, ], [ 'itemtype' => 'post', // Blog posts. 'component' => 'core', 'callback' => 'blog_get_tagged_posts', 'callbackfile' => '/blog/lib.php', ], [ 'itemtype' => 'blog_external', // External blogs. 'component' => 'core', ], [ 'itemtype' => 'course_modules', // Course modules. 'component' => 'core', 'callback' => 'course_get_tagged_course_modules', 'callbackfile' => '/course/lib.php', ], [ 'itemtype' => 'badge', // Badges. 'component' => 'core_badges', 'callback' => 'badge_get_tagged_badges', 'callbackfile' => '/badges/lib.php', ], [ 'itemtype' => 'reportbuilder_report', 'component' => 'core_reportbuilder', 'callback' => 'core_reportbuilder_get_tagged_reports', 'callbackfile' => '/reportbuilder/lib.php', ], ]; messageinbound_handlers.php000064400000002040152267074040012143 0ustar00. /** * Inbound Message Handlers for core. * * @package core_message * @copyright 2014 Andrew NIcols * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $handlers = array( array( 'classname' => '\core\message\inbound\private_files_handler', 'defaultexpiration' => 0, ), ); analytics.php000064400000012022152267174020007247 0ustar00. /** * Defines the built-in prediction models provided by the Moodle core. * * @package core * @category analytics * @copyright 2019 David Mudrák * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $models = [ [ 'target' => '\core_course\analytics\target\course_dropout', 'indicators' => [ '\core\analytics\indicator\any_access_after_end', '\core\analytics\indicator\any_access_before_start', '\core\analytics\indicator\any_write_action_in_course', '\core\analytics\indicator\read_actions', '\core_course\analytics\indicator\completion_enabled', '\core_course\analytics\indicator\potential_cognitive_depth', '\core_course\analytics\indicator\potential_social_breadth', '\mod_assign\analytics\indicator\cognitive_depth', '\mod_assign\analytics\indicator\social_breadth', '\mod_book\analytics\indicator\cognitive_depth', '\mod_book\analytics\indicator\social_breadth', '\mod_choice\analytics\indicator\cognitive_depth', '\mod_choice\analytics\indicator\social_breadth', '\mod_data\analytics\indicator\cognitive_depth', '\mod_data\analytics\indicator\social_breadth', '\mod_feedback\analytics\indicator\cognitive_depth', '\mod_feedback\analytics\indicator\social_breadth', '\mod_folder\analytics\indicator\cognitive_depth', '\mod_folder\analytics\indicator\social_breadth', '\mod_forum\analytics\indicator\cognitive_depth', '\mod_forum\analytics\indicator\social_breadth', '\mod_glossary\analytics\indicator\cognitive_depth', '\mod_glossary\analytics\indicator\social_breadth', '\mod_imscp\analytics\indicator\cognitive_depth', '\mod_imscp\analytics\indicator\social_breadth', '\mod_label\analytics\indicator\cognitive_depth', '\mod_label\analytics\indicator\social_breadth', '\mod_lesson\analytics\indicator\cognitive_depth', '\mod_lesson\analytics\indicator\social_breadth', '\mod_lti\analytics\indicator\cognitive_depth', '\mod_lti\analytics\indicator\social_breadth', '\mod_page\analytics\indicator\cognitive_depth', '\mod_page\analytics\indicator\social_breadth', '\mod_quiz\analytics\indicator\cognitive_depth', '\mod_quiz\analytics\indicator\social_breadth', '\mod_resource\analytics\indicator\cognitive_depth', '\mod_resource\analytics\indicator\social_breadth', '\mod_scorm\analytics\indicator\cognitive_depth', '\mod_scorm\analytics\indicator\social_breadth', '\mod_url\analytics\indicator\cognitive_depth', '\mod_url\analytics\indicator\social_breadth', '\mod_wiki\analytics\indicator\cognitive_depth', '\mod_wiki\analytics\indicator\social_breadth', '\mod_workshop\analytics\indicator\cognitive_depth', '\mod_workshop\analytics\indicator\social_breadth', ], ], [ 'target' => '\core_course\analytics\target\no_teaching', 'indicators' => [ '\core_course\analytics\indicator\no_teacher', '\core_course\analytics\indicator\no_student', ], 'timesplitting' => '\core\analytics\time_splitting\single_range', 'enabled' => true, ], [ 'target' => '\core_user\analytics\target\upcoming_activities_due', 'indicators' => [ '\core_course\analytics\indicator\activities_due', ], 'timesplitting' => '\core\analytics\time_splitting\upcoming_week', 'enabled' => true, ], [ 'target' => '\core_course\analytics\target\no_access_since_course_start', 'indicators' => [ '\core\analytics\indicator\any_course_access', ], 'timesplitting' => '\core\analytics\time_splitting\one_month_after_start', 'enabled' => true, ], [ 'target' => '\core_course\analytics\target\no_recent_accesses', 'indicators' => [ '\core\analytics\indicator\any_course_access', ], 'timesplitting' => '\core\analytics\time_splitting\past_month', 'enabled' => true, ], ]; index.html000064400000000001152267174020006536 0ustar00 legacyclasses.php000064400000026520152267174020010112 0ustar00. /** * This file contains mappings for legacy classes that do not fit the standard class naming conventions. * * In time these classes should be renamed to fit the standard class naming conventions but this is not an overnight process. * * @package core * @copyright Andrew Lyons * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); // Like other files in the db directory this file uses an array. // The old class name is the key, the path to the file containing the class is the value. // The array must be called $legacyclasses. $legacyclasses = [ \bootstrap_renderer::class => 'output/bootstrap_renderer.php', // Core API. \emoticon_manager::class => 'emoticon_manager.php', \lang_string::class => 'lang_string.php', \moodle_url::class => 'url.php', // Exception API. \coding_exception::class => 'exception/coding_exception.php', \file_serving_exception::class => 'exception/file_serving_exception.php', \invalid_dataroot_permissions::class => 'exception/invalid_dataroot_permissions.php', \invalid_parameter_exception::class => 'exception/invalid_parameter_exception.php', \invalid_response_exception::class => 'exception/invalid_response_exception.php', \invalid_state_exception::class => 'exception/invalid_state_exception.php', \moodle_exception::class => 'exception/moodle_exception.php', \require_login_exception::class => 'exception/require_login_exception.php', \require_login_session_timeout_exception::class => 'exception/require_login_session_timeout_exception.php', \required_capability_exception::class => 'exception/required_capability_exception.php', \webservice_parameter_exception::class => 'exception/webservice_parameter_exception.php', // Cache API. \cache::class => [ 'core_cache', 'cache.php', ], \cache_application::class => [ 'core_cache', 'application_cache.php', ], \cache_definition::class => [ 'core_cache', 'definition.php', ], \cache_request::class => [ 'core_cache', 'request_cache.php', ], \cache_session::class => [ 'core_cache', 'session_cache.php', ], \cache_cached_object::class => [ 'core_cache', 'cached_object.php', ], \cache_config::class => [ 'core_cache', 'config.php', ], \cache_config_writer::class => [ 'core_cache', 'config_writer.php', ], \cache_config_disabled::class => [ 'core_cache', 'disabled_config.php', ], \cache_disabled::class => [ 'core_cache', 'disabled_cache.php', ], \config_writer::class => [ 'core_cache', 'config_writer.php', ], \cache_data_source::class => [ 'core_cache', 'data_source_interface.php', ], \cache_data_source_versionable::class => [ 'core_cache', 'versionable_data_source_interface.php', ], \cache_exception::class => [ 'core_cache', 'exception/cache_exception.php', ], \cache_factory::class => [ 'core_cache', 'factory.php', ], \cache_factory_disabled::class => [ 'core_cache', 'disabled_factory.php', ], \cache_helper::class => [ 'core_cache', 'helper.php', ], \cache_is_key_aware::class => [ 'core_cache', 'key_aware_cache_interface.php', ], \cache_is_lockable::class => [ 'core_cache', 'lockable_cache_interface.php', ], \cache_is_searchable::class => [ 'core_cache', 'searchable_cache_interface.php', ], \cache_is_configurable::class => [ 'core_cache', 'configurable_cache_interface.php', ], \cache_loader::class => [ 'core_cache', 'loader_interface.php', ], \cache_loader_with_locking::class => [ 'core_cache', 'loader_with_locking_interface.php', ], \cache_lock_interface::class => [ 'core_cache', 'cache_lock_interface.php', ], \cache_store::class => [ 'core_cache', 'store.php', ], \cache_store_interface::class => [ 'core_cache', 'store_interface.php', ], \cache_ttl_wrapper::class => [ 'core_cache', 'ttl_wrapper.php', ], \cacheable_object::class => [ 'core_cache', 'cacheable_object_interface.php', ], \cacheable_object_array::class => [ 'core_cache', 'cacheable_object_array.php', ], \cache_definition_mappings_form::class => [ 'core_cache', 'form/cache_definition_mappings_form.php', ], \cache_definition_sharing_form::class => [ 'core_cache', 'form/cache_definition_sharing_form.php', ], \cache_lock_form::class => [ 'core_cache', 'form/cache_lock_form.php', ], \cache_mode_mappings_form::class => [ 'core_cache', 'form/cache_mode_mappings_form.php', ], \cachestore_addinstance_form::class => [ 'core_cache', 'form/cachestore_addinstance_form.php', ], // Output API. \theme_config::class => 'output/theme_config.php', \xhtml_container_stack::class => 'output/xhtml_container_stack.php', \renderable::class => 'output/renderable.php', \templatable::class => 'output/templatable.php', // Output API: Renderer Factories. \renderer_factory::class => 'output/renderer_factory/renderer_factory_interface.php', \renderer_factory_base::class => 'output/renderer_factory/renderer_factory_base.php', \standard_renderer_factory::class => 'output/renderer_factory/standard_renderer_factory.php', \theme_overridden_renderer_factory::class => 'output/renderer_factory/theme_overridden_renderer_factory.php', // Output API: Renderers. \renderer_base::class => 'output/renderer_base.php', \plugin_renderer_base::class => 'output/plugin_renderer_base.php', \core_renderer::class => 'output/core_renderer.php', \core_renderer_cli::class => 'output/core_renderer_cli.php', \core_renderer_ajax::class => 'output/core_renderer_ajax.php', \core_renderer_maintenance::class => 'output/core_renderer_maintenance.php', \page_requirements_manager::class => 'output/requirements/page_requirements_manager.php', \YUI_config::class => 'output/requirements/yui.php', \fragment_requirements_manager::class => 'output/requirements/fragment_requirements_manager.php', // Output API: components. \file_picker::class => 'output/file_picker.php', \user_picture::class => 'output/user_picture.php', \help_icon::class => 'output/help_icon.php', \pix_icon_font::class => 'output/pix_icon_font.php', \pix_icon_fontawesome::class => 'output/pix_icon_fontawesome.php', \pix_icon::class => 'output/pix_icon.php', \image_icon::class => 'output/image_icon.php', \pix_emoticon::class => 'output/pix_emoticon.php', \single_button::class => 'output/single_button.php', \single_select::class => 'output/single_select.php', \url_select::class => 'output/url_select.php', \action_link::class => 'output/action_link.php', \html_writer::class => 'output/html_writer.php', \js_writer::class => 'output/js_writer.php', \paging_bar::class => 'output/paging_bar.php', \initials_bar::class => 'output/initials_bar.php', \custom_menu_item::class => 'output/custom_menu_item.php', \custom_menu::class => 'output/custom_menu.php', \tabobject::class => 'output/tabobject.php', \context_header::class => 'output/context_header.php', \tabtree::class => 'output/tabtree.php', \action_menu::class => 'output/action_menu.php', \action_menu_filler::class => 'output/action_menu/filler.php', \action_menu_link::class => 'output/action_menu/link.php', \action_menu_link_primary::class => 'output/action_menu/link_primary.php', \action_menu_link_secondary::class => 'output/action_menu/link_secondary.php', \core\output\local\action_menu\subpanel::class => 'output/action_menu/subpanel.php', \preferences_groups::class => 'output/preferences_groups.php', \preferences_group::class => 'output/preferences_group.php', \progress_bar::class => 'output/progress_bar.php', \component_action::class => 'output/actions/component_action.php', \confirm_action::class => 'output/actions/confirm_action.php', \popup_action::class => 'output/actions/popup_action.php', // Block Subsystem. \block_contents::class => [ 'core_block', 'output/block_contents.php', ], \block_move_target::class => [ 'core_block', 'output/block_move_target.php', ], // Table Subsystem. \html_table::class => [ 'core_table', 'output/html_table.php', ], \html_table_row::class => [ 'core_table', 'output/html_table_row.php', ], \html_table_cell::class => [ 'core_table', 'output/html_table_cell.php', ], \flexible_table::class => [ 'core_table', 'flexible_table.php', ], \table_sql::class => [ 'core_table', 'sql_table.php', ], \table_default_export_format_parent::class => [ 'core_table', 'base_export_format.php', ], \table_dataformat_export_format::class => [ 'core_table', 'dataformat_export_format.php', ], // The progress_trace classes. \combined_progress_trace::class => 'output/progress_trace/combined_progress_trace.php', \error_log_progress_trace::class => 'output/progress_trace/error_log_progress_trace.php', \html_list_progress_trace::class => 'output/progress_trace/html_list_progress_trace.php', \html_progress_trace::class => 'output/progress_trace/html_progress_trace.php', \null_progress_trace::class => 'output/progress_trace/null_progress_trace.php', \progress_trace::class => 'output/progress_trace.php', \progress_trace_buffer::class => 'output/progress_trace/progress_trace_buffer.php', \text_progress_trace::class => 'output/progress_trace/text_progress_trace.php', // Filters subsystem. \filter_manager::class => [ 'core_filters', 'filter_manager.php', ], \filterobject::class => [ 'core_filters', 'filter_object.php', ], \moodle_text_filter::class => [ 'core_filters', 'text_filter.php', ], \null_filter_manager::class => [ 'core_filters', 'null_filter_manager.php', ], \performance_measuring_filter_manager::class => [ 'core_filters', 'performance_measuring_filter_manager.php', ], \filter_local_settings_form::class => [ 'core_filters', 'form/local_settings_form.php', ], ];