ÿØÿà 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-Ї Ç PKx\韕lib.phpnu[. /** * Adds data generator support * * @package core * @category test * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ // NOTE: MOODLE_INTERNAL is not verified here because we load this before setup.php! require_once(__DIR__.'/data_generator.php'); require_once(__DIR__.'/component_generator_base.php'); require_once(__DIR__.'/module_generator.php'); require_once(__DIR__.'/block_generator.php'); require_once(__DIR__.'/default_block_generator.php'); require_once(__DIR__.'/repository_generator.php'); PK5\=.33#behat_core_competency_generator.phpnu[. use core_competency\competency; use core_competency\competency_framework; use core_competency\plan; /** * Behat data generator for core_competency. * * @package core_competency * @category test * @copyright 2022 Noel De Martin * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class behat_core_competency_generator extends behat_generator_base { /** * Get a list of the entities that Behat can create using the generator step. * * @return array */ protected function get_creatable_entities(): array { return [ 'competencies' => [ 'singular' => 'competency', 'datagenerator' => 'competency', 'required' => ['shortname', 'competencyframework'], 'switchids' => ['competencyframework' => 'competencyframeworkid'], ], 'course_competencies' => [ 'singular' => 'course_competency', 'datagenerator' => 'course_competency', 'required' => ['course', 'competency'], 'switchids' => ['course' => 'courseid', 'competency' => 'competencyid'], ], 'frameworks' => [ 'singular' => 'framework', 'datagenerator' => 'framework', 'required' => ['shortname'], 'switchids' => ['scale' => 'scaleid'], ], 'plans' => [ 'singular' => 'plan', 'datagenerator' => 'plan', 'required' => ['name'], 'switchids' => ['user' => 'userid', 'status' => 'status'], ], 'plan_competencies' => [ 'singular' => 'plan_competency', 'datagenerator' => 'plan_competency', 'required' => ['plan', 'competency'], 'switchids' => ['competency' => 'competencyid', 'plan' => 'planid'], ], 'related_competencies' => [ 'singular' => 'related_competency', 'datagenerator' => 'related_competency', 'required' => ['competency', 'relatedcompetency'], 'switchids' => ['competency' => 'competencyid', 'relatedcompetency' => 'relatedcompetencyid'], ], 'user_competency' => [ 'singular' => 'user_competency', 'datagenerator' => 'user_competency', 'required' => ['competency', 'user'], 'switchids' => ['competency' => 'competencyid', 'user' => 'userid'], ], 'user_competency_courses' => [ 'singular' => 'user_competency_course', 'datagenerator' => 'user_competency_course', 'required' => ['course', 'competency', 'user'], 'switchids' => ['course' => 'courseid', 'competency' => 'competencyid', 'user' => 'userid'], ], 'user_competency_plans' => [ 'singular' => 'user_competency_plan', 'datagenerator' => 'user_competency_plan', 'required' => ['plan', 'competency', 'user'], 'switchids' => ['plan' => 'planid', 'competency' => 'competencyid', 'user' => 'userid'], ], 'user_evidence' => [ 'singular' => 'user_evidence', 'datagenerator' => 'user_evidence', 'required' => ['user', 'name'], 'switchids' => ['user' => 'userid'], ], 'user_evidence_competency' => [ 'singular' => 'user_evidence_competency', 'datagenerator' => 'user_evidence_competency', 'required' => ['userevidence', 'competency'], 'switchids' => ['userevidence' => 'userevidenceid', 'competency' => 'competencyid'], ], 'templates' => [ 'singular' => 'template', 'datagenerator' => 'template', 'required' => ['shortname'], 'switchids' => ['context' => 'contextid'], ], 'template_competencies' => [ 'singular' => 'template_competency', 'datagenerator' => 'template_competency', 'required' => ['template', 'competency'], 'switchids' => ['template' => 'templateid', 'competency' => 'competencyid'], ], ]; } /** * Get the competency framework id using an idnumber. * * @param string $idnumber * @return int The competency framework id */ protected function get_competencyframework_id(string $idnumber): int { global $DB; if (!$id = $DB->get_field('competency_framework', 'id', ['idnumber' => $idnumber])) { throw new Exception('The specified competency framework with idnumber "' . $idnumber . '" could not be found.'); } return $id; } /** * Get the competency id using an idnumber. * * @param string $idnumber * @return int The competency id */ protected function get_competency_id(string $idnumber): int { global $DB; if (!$id = $DB->get_field('competency', 'id', ['idnumber' => $idnumber])) { throw new Exception('The specified competency with idnumber "' . $idnumber . '" could not be found.'); } return $id; } /** * Get the learning plan id using a name. * * @param string $name * @return int The learning plan id */ protected function get_plan_id(string $name): int { global $DB; if (!$id = $DB->get_field('competency_plan', 'id', ['name' => $name])) { throw new Exception('The specified learning plan with name "' . $name . '" could not be found.'); } return $id; } /** * Get the related competency id using an idnumber. * * @param string $idnumber * @return int The related competency id */ protected function get_relatedcompetency_id(string $idnumber): int { return $this->get_competency_id($idnumber); } /** * Get the template id by shortname. * * @param string $shortname The template name. * @return int */ protected function get_template_id(string $shortname): int { global $DB; if (!$id = $DB->get_field('competency_template', 'id', ['shortname' => $shortname])) { throw new Exception('The specified template with name "' . $shortname . '" could not be found.'); } return $id; } /** * Add a plan. * * @param array $data Plan data. */ public function process_plan(array $data): void { $generator = $this->get_data_generator(); $competencyids = $data['competencyids'] ?? []; unset($data['competencyids']); $plan = $generator->create_plan($data); foreach ($competencyids as $competencyid) { $generator->create_plan_competency([ 'planid' => $plan->get('id'), 'competencyid' => $competencyid, ]); } } /** * Preprocess user competency data. * * @param array $data Raw data. * @return array Processed data. */ protected function preprocess_user_competency(array $data): array { $this->prepare_grading($data); return $data; } /** * Preprocess user course competency data. * * @param array $data Raw data. * @return array Processed data. */ protected function preprocess_user_competency_course(array $data): array { $this->prepare_grading($data); return $data; } /** * Preprocess user learning plan competency data. * * @param array $data Raw data. * @return array Processed data. */ protected function preprocess_user_competency_plan(array $data): array { $this->prepare_grading($data); return $data; } /** * Preprocess plan data. * * @param array $data Raw data. * @return array Processed data. */ protected function preprocess_plan(array $data): array { global $DB, $USER; if (isset($data['competencies'])) { $competencies = array_map('trim', str_getcsv($data['competencies'], escape: '\\')); $data['competencyids'] = array_map([$this, 'get_competency_id'], $competencies); unset($data['competencies']); } if (isset($data['reviewer'])) { if (is_number($data['reviewer'])) { $data['reviewerid'] = $data['reviewer']; } else { if (!$userid = $DB->get_field('user', 'id', ['username' => $data['reviewer']])) { throw new Exception('The specified user "' . $data['reviewer'] . '" could not be found.'); } $data['reviewerid'] = $userid; } unset($data['reviewer']); } return $data + [ 'userid' => $USER->id, ]; } /** * Prepare grading attributes for record data. * * @param array $data Record data. */ protected function prepare_grading(array &$data): void { if (!isset($data['grade'])) { return; } global $DB; $competency = competency::get_record(['id' => $data['competencyid']]); $competencyframework = competency_framework::get_record(['id' => $competency->get('competencyframeworkid')]); $scale = $DB->get_field('scale', 'scale', ['id' => $competencyframework->get('scaleid')]); $grades = array_map('trim', explode(',', $scale)); $grade = array_search($data['grade'], $grades); if ($grade === false) { throw new Exception('The grade "'.$data['grade'].'" was not found in the "'. $competencyframework->get('shortname').'" competency framework.'); } $data['proficiency'] = true; $data['grade'] = $grade + 1; } /** * Get the module data generator. * * @return core_competency_generator Competency data generator. */ protected function get_data_generator(): core_competency_generator { return $this->componentdatagenerator; } /** * Get the user evidence id using a name. * * @param string $name User evidence name. * @return int The user evidence id */ protected function get_userevidence_id(string $name): int { global $DB; if (!$id = $DB->get_field('competency_userevidence', 'id', ['name' => $name])) { throw new Exception('The specified user evidence with name "' . $name . '" could not be found.'); } return $id; } /** * Get the template competency id using a name. * * @param string $name Template competency name. * @return int The template competency id */ protected function get_templatecompetency_id(string $name): int { global $DB; if (!$id = $DB->get_field('competency_template', 'id', ['name' => $name])) { throw new Exception('The specified template competency with name "' . $name . '" could not be found.'); } return $id; } /** * Get the context id using a contextid. * * @param string $contextid Context id. * @return int The context id */ protected function get_context_id(string $contextid): int { global $DB; if (!$id = $DB->get_field('context', 'id', ['id' => $contextid])) { throw new Exception('The specified context with id "' . $contextid . '" could not be found.'); } return $id; } /** * Get the status id by status name. * * @param string $name Status name. * @return int */ protected function get_status_id(string $name): int { switch ($name) { case 'draft': $status = plan::STATUS_DRAFT; break; case 'in review': $status = plan::STATUS_IN_REVIEW; break; case 'waiting for review': $status = plan::STATUS_WAITING_FOR_REVIEW; break; case 'complete': $status = plan::STATUS_COMPLETE; break; default: $status = plan::STATUS_ACTIVE; break; } return $status; } } PKN\EA  behat_core_payment_generator.phpnu[. /** * Behat data generator for core_payment. * * @package core_payment * @category test * @copyright 2020 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Behat data generator for core_payment. * * @package core_payment * @category test * @copyright 2020 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class behat_core_payment_generator extends behat_generator_base { /** * Get a list of the entities that can be created. * * @return array entity name => information about how to generate. */ protected function get_creatable_entities(): array { return [ 'payment accounts' => [ 'singular' => 'payment account', 'datagenerator' => 'payment_account', 'required' => ['name'], ], 'payments' => [ 'singular' => 'payment', 'datagenerator' => 'payment', 'required' => ['account', 'amount', 'user'], 'switchids' => ['account' => 'accountid', 'user' => 'userid'], ], ]; } /** * Look up the id of a account from its name. * * @param string $accountname * @return int corresponding id. */ protected function get_account_id(string $accountname): int { global $DB; if (!$id = $DB->get_field('payment_accounts', 'id', ['name' => $accountname])) { throw new Exception('There is no account with name "' . $accountname . '".'); } return $id; } } PKz\ c behat_mod_quiz_generator.phpnu[. /** * Behat data generator for mod_quiz. * * @package mod_quiz * @category test * @copyright 2019 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Behat data generator for mod_quiz. * * @copyright 2019 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class behat_mod_quiz_generator extends behat_generator_base { protected function get_creatable_entities(): array { return [ 'group overrides' => [ 'singular' => 'group override', 'datagenerator' => 'override', 'required' => ['quiz', 'group'], 'switchids' => ['quiz' => 'quiz', 'group' => 'groupid'], ], 'user overrides' => [ 'singular' => 'user override', 'datagenerator' => 'override', 'required' => ['quiz', 'user'], 'switchids' => ['quiz' => 'quiz', 'user' => 'userid'], ], 'grade items' => [ 'singular' => 'grade item', 'datagenerator' => 'grade_item', 'required' => ['quiz', 'name'], 'switchids' => ['quiz' => 'quizid'], ], ]; } /** * Look up the id of a quiz from its name. * * @param string $quizname the quiz name, for example 'Test quiz'. * @return int corresponding id. */ protected function get_quiz_id(string $quizname): int { global $DB; if (!$id = $DB->get_field('quiz', 'id', ['name' => $quizname])) { throw new Exception('There is no quiz with name "' . $quizname . '" does not exist'); } return $id; } } PK\behat_mod_lti_generator.phpnu[. defined('MOODLE_INTERNAL') || die(); /** * Define behat generator for mod_lti. * * @package mod_lti * @category test * @author Andrew Madden * @copyright 2020 Catalyst IT * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class behat_mod_lti_generator extends behat_generator_base { /** * Get list of entities for mod_lti behat tests. * * @return array[] List of entity definitions. */ protected function get_creatable_entities(): array { return [ 'tool proxies' => [ 'singular' => 'tool proxy', 'datagenerator' => 'tool_proxies', 'required' => [], ], 'tool types' => [ 'singular' => 'tool type', 'datagenerator' => 'tool_types', 'required' => ['baseurl'], 'switchids' => ['lti_coursecategories' => 'lti_coursecategories'] ], 'course tools' => [ 'singular' => 'course tool', 'datagenerator' => 'course_tool_types', 'required' => ['baseurl', 'course'], 'switchids' => ['course' => 'course'] ], 'tool instances' => [ 'singular' => 'instance', 'datagenerator' => 'instance', 'required' => ['course', 'tool'], 'switchids' => ['course' => 'course', 'tool' => 'typeid'] ], ]; } /** * Handles the switchid ['tool' => 'typeid'] for finding a tool by name. * * @param string $name the name of the tool. * @return int the id of the tool type identified by the name $name. */ protected function get_tool_id(string $name): int { global $DB; if (!$id = $DB->get_field('lti_types', 'id', ['name' => $name])) { throw new coding_exception('The specified tool with name "' . $name . '" does not exist'); } return (int) $id; } /** * Handles the switchid ['lti_coursecategories' => 'lti_coursecategories'] for restricting a tool to certain categories. * * @param string $idnumbers a comma-separated string containing the course category id numbers, e.g. 'cata, catb, catc'. * @return string a comma-separated string containing the course category ids. * @throws coding_exception if one or more of the categories is unable to be matched by its idnumber. */ protected function get_lti_coursecategories_id(string $idnumbers): string { global $DB; $categoryids = array_map('trim', explode(',', $idnumbers)); [$insql, $inparams] = $DB->get_in_or_equal($categoryids); $ids = $DB->get_fieldset_sql("SELECT id FROM {course_categories} WHERE idnumber $insql", $inparams); if (!$ids || count($ids) != count($categoryids)) { throw new coding_exception("One or more course categories unable to be matched using idnumbers: $idnumbers"); } return implode(',', $ids); } } PKY\)mHHbehat_core_ai_generator.phpnu[. /** * Behat generator for AI. * * @package core_ai * @copyright 2024 David Woloszyn * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class behat_core_ai_generator extends behat_generator_base { /** * Get the list of creatable entities for core_ai. * * @return array */ protected function get_creatable_entities(): array { return [ 'ai actions' => [ 'singular' => 'ai action', 'datagenerator' => 'ai_actions', 'required' => [ 'actionname', 'success', 'user', 'contextid', 'provider', ], 'switchids' => [ 'user' => 'userid', ], ], 'ai providers' => [ 'singular' => 'ai provider', 'datagenerator' => 'ai_provider', 'required' => [ 'provider', 'name', 'enabled', ], ], ]; } } PK\~ZZcomponent_generator_base.phpnu[. /** * Component generator base class. * * @package core * @category test * @copyright 2013 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Component generator base class. * * Extend in path/to/component/tests/generator/lib.php as * class type_plugin_generator extends component_generator_base * Note that there are more specific classes to extend for mods and blocks. * * @copyright 2013 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class component_generator_base { /** * @var testing_data_generator */ protected $datagenerator; /** * Constructor. * @param testing_data_generator $datagenerator */ public function __construct(testing_data_generator $datagenerator) { $this->datagenerator = $datagenerator; } /** * To be called from data reset code only, * do not use in tests. * @return void */ public function reset() { } /** * Set the current user during data generation. * * This should be avoided wherever possible, but in some situations underlying code will insert data as the current * user. * * @param stdClass $user */ protected function set_user(?stdClass $user = null): void { global $CFG, $DB; if ($user === null) { $user = (object) [ 'id' => 0, 'mnethostid' => $CFG->mnet_localhost_id, ]; } else { $user = clone($user); unset($user->description); unset($user->access); unset($user->preference); } // Ensure session is empty, as it may contain caches and user-specific info. \core\session\manager::init_empty_session(); \core\session\manager::set_user($user); } /** * Update the instance record, inserting any files that are referenced. * * @param stdClass $instance The instance record of the already-created record * @param stdClass $record The data passed in to create the instance * @param string $table The table that the data exists in * @param context $context The context of the instance * @param string $component The component of the owning plugin * @param string $filearea The name of the file area * @param int $targetitemid The itemid to use when saving the files * @return stdClass The updated instance record */ protected function insert_files( stdClass $instance, stdClass $record, string $table, context $context, string $component, string $filearea, int $targetitemid ): stdClass { global $CFG, $DB, $USER; $fieldname = "[[files::{$filearea}]]"; if (!isset($record->$fieldname)) { return $instance; } preg_match('/\[\[files::(.*)\]\]/', $fieldname, $matches); if (empty($matches[1])) { throw new coding_exception('Invalid file field name: ' . $fieldname); } $referencedfieldname = trim($matches[1]); if (!isset($record->$referencedfieldname)) { throw new coding_exception("File field '{$fieldname}' references non-existent field '{$referencedfieldname}'"); } $fs = get_file_storage(); $itemid = file_get_unused_draft_itemid(); $itemidfieldname = "{$referencedfieldname}[itemid]"; $record->$itemidfieldname = $itemid; $filenames = explode(',', $record->$fieldname); foreach ($filenames as $filename) { $filename = trim($filename); if (!$filename) { continue; } $explodedfilename = explode('::', $filename, 3); if (count($explodedfilename) === 2) { [$sourcefile, $targetfile] = $explodedfilename; $user = $USER; } else { [$sourcefile, $targetfile, $username] = $explodedfilename; $user = \core_user::get_user_by_username($username); } $filepath = "{$CFG->dirroot}/{$sourcefile}"; if (!file_exists($filepath)) { throw new coding_exception("File '{$filepath}' does not exist"); } $filerecord = [ 'userid' => $user->id, 'contextid' => context_user::instance($user->id)->id, 'component' => 'user', 'filearea' => 'draft', 'itemid' => $itemid, 'filepath' => '/' . dirname($targetfile), 'filename' => basename($targetfile), ]; $fs->create_file_from_pathname($filerecord, $filepath); } $olduser = $USER; $this->set_user($user); $instance->$referencedfieldname = file_save_draft_area_files( $itemid, $context->id, $component, $referencedfieldname, $targetitemid, null, $instance->$referencedfieldname ); $this->set_user($olduser); $DB->update_record($table, $instance); return $instance; } } PK\E data_generator.phpnu[. defined('MOODLE_INTERNAL') || die(); /** * Data generator class for unit tests and other tools that need to create fake test sites. * * @package core * @category test * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class testing_data_generator { /** @var int The number of grade categories created */ protected $gradecategorycounter = 0; /** @var int The number of grade items created */ protected $gradeitemcounter = 0; /** @var int The number of grade outcomes created */ protected $gradeoutcomecounter = 0; protected $usercounter = 0; protected $categorycount = 0; protected $cohortcount = 0; protected $coursecount = 0; protected $scalecount = 0; protected $groupcount = 0; protected $groupingcount = 0; protected $rolecount = 0; protected $tagcount = 0; /** @var array list of plugin generators */ protected $generators = array(); /** @var array lis of common last names */ public $lastnames = array( 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'García', 'Rodríguez', 'Wilson', 'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Meyer', 'Weber', 'Schulz', 'Wagner', 'Becker', 'Hoffmann', 'Novák', 'Svoboda', 'Novotný', 'Dvořák', 'Černý', 'Procházková', 'Kučerová', 'Veselá', 'Horáková', 'Němcová', 'Смирнов', 'Иванов', 'Кузнецов', 'Соколов', 'Попов', 'Лебедева', 'Козлова', 'Новикова', 'Морозова', 'Петрова', '王', '李', '张', '刘', '陈', '楊', '黃', '趙', '吳', '周', '佐藤', '鈴木', '高橋', '田中', '渡辺', '伊藤', '山本', '中村', '小林', '斎藤', ); /** @var array lis of common first names */ public $firstnames = array( 'Jacob', 'Ethan', 'Michael', 'Jayden', 'William', 'Isabella', 'Sophia', 'Emma', 'Olivia', 'Ava', 'Lukas', 'Leon', 'Luca', 'Timm', 'Paul', 'Leonie', 'Leah', 'Lena', 'Hanna', 'Laura', 'Jakub', 'Jan', 'Tomáš', 'Lukáš', 'Matěj', 'Tereza', 'Eliška', 'Anna', 'Adéla', 'Karolína', 'Даниил', 'Максим', 'Артем', 'Иван', 'Александр', 'София', 'Анастасия', 'Дарья', 'Мария', 'Полина', '伟', '伟', '芳', '伟', '秀英', '秀英', '娜', '秀英', '伟', '敏', '翔', '大翔', '拓海', '翔太', '颯太', '陽菜', 'さくら', '美咲', '葵', '美羽', ); public $loremipsum = <<gradecategorycounter = 0; $this->gradeitemcounter = 0; $this->gradeoutcomecounter = 0; $this->usercounter = 0; $this->categorycount = 0; $this->cohortcount = 0; $this->coursecount = 0; $this->scalecount = 0; $this->groupcount = 0; $this->groupingcount = 0; $this->rolecount = 0; $this->tagcount = 0; foreach ($this->generators as $generator) { $generator->reset(); } } /** * Return generator for given plugin or component. * @param string $component the component name, e.g. 'mod_forum' or 'core_question'. * @return component_generator_base or rather an instance of the appropriate subclass. */ public function get_plugin_generator($component) { // Note: This global is included so that generator have access to it. // CFG is widely used in require statements. global $CFG; list($type, $plugin) = core_component::normalize_component($component); $cleancomponent = $type . '_' . $plugin; if ($cleancomponent != $component) { debugging("Please specify the component you want a generator for as " . "{$cleancomponent}, not {$component}.", DEBUG_DEVELOPER); $component = $cleancomponent; } if (isset($this->generators[$component])) { return $this->generators[$component]; } $dir = core_component::get_component_directory($component); $lib = $dir . '/tests/generator/lib.php'; if (!$dir || !is_readable($lib)) { $this->generators[$component] = $this->get_default_plugin_generator($component); return $this->generators[$component]; } include_once($lib); $classname = $component . '_generator'; if (class_exists($classname)) { $this->generators[$component] = new $classname($this); } else { $this->generators[$component] = $this->get_default_plugin_generator($component, $classname); } return $this->generators[$component]; } /** * Create a test user * @param array|stdClass $record * @param array $options * @return stdClass user record */ public function create_user($record=null, ?array $options=null) { global $DB, $CFG; require_once($CFG->dirroot.'/user/lib.php'); $this->usercounter++; $i = $this->usercounter; $record = (array)$record; if (!isset($record['auth'])) { $record['auth'] = 'manual'; } if (!isset($record['firstname']) and !isset($record['lastname'])) { $country = rand(0, 5); $firstname = rand(0, 4); $lastname = rand(0, 4); $female = rand(0, 1); $record['firstname'] = $this->firstnames[($country*10) + $firstname + ($female*5)]; $record['lastname'] = $this->lastnames[($country*10) + $lastname + ($female*5)]; } else if (!isset($record['firstname'])) { $record['firstname'] = 'Firstname'.$i; } else if (!isset($record['lastname'])) { $record['lastname'] = 'Lastname'.$i; } if (!isset($record['firstnamephonetic'])) { $firstnamephonetic = rand(0, 59); $record['firstnamephonetic'] = $this->firstnames[$firstnamephonetic]; } if (!isset($record['lastnamephonetic'])) { $lastnamephonetic = rand(0, 59); $record['lastnamephonetic'] = $this->lastnames[$lastnamephonetic]; } if (!isset($record['middlename'])) { $middlename = rand(0, 59); $record['middlename'] = $this->firstnames[$middlename]; } if (!isset($record['alternatename'])) { $alternatename = rand(0, 59); $record['alternatename'] = $this->firstnames[$alternatename]; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } if (!isset($record['mnethostid'])) { $record['mnethostid'] = $CFG->mnet_localhost_id; } if (!isset($record['username'])) { $record['username'] = 'username'.$i; $j = 2; while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) { $record['username'] = 'username'.$i.'_'.$j; $j++; } } if (isset($record['password'])) { $record['password'] = hash_internal_user_password($record['password']); } if (!isset($record['email'])) { $record['email'] = $record['username'].'@example.com'; } if (!isset($record['confirmed'])) { $record['confirmed'] = 1; } if (!isset($record['lastip'])) { $record['lastip'] = '0.0.0.0'; } $tobedeleted = !empty($record['deleted']); unset($record['deleted']); $userid = user_create_user($record, false, false); if ($extrafields = array_intersect_key($record, ['password' => 1, 'timecreated' => 1])) { $DB->update_record('user', ['id' => $userid] + $extrafields); } if (!$tobedeleted) { // All new not deleted users must have a favourite self-conversation. $selfconversation = \core_message\api::create_conversation( \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF, [$userid] ); \core_message\api::set_favourite_conversation($selfconversation->id, $userid); // Save custom profile fields data. $hasprofilefields = array_filter($record, function($key){ return strpos($key, 'profile_field_') === 0; }, ARRAY_FILTER_USE_KEY); if ($hasprofilefields) { require_once($CFG->dirroot.'/user/profile/lib.php'); $usernew = (object)(['id' => $userid] + $record); profile_save_data($usernew); } } $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); if (!$tobedeleted && isset($record['interests'])) { require_once($CFG->dirroot . '/user/editlib.php'); if (!is_array($record['interests'])) { $record['interests'] = preg_split('/\s*,\s*/', trim($record['interests']), -1, PREG_SPLIT_NO_EMPTY); } useredit_update_interests($user, $record['interests']); } \core\event\user_created::create_from_userid($userid)->trigger(); if ($tobedeleted) { delete_user($user); $user = $DB->get_record('user', array('id' => $userid)); } return $user; } /** * Create a test course category * @param array|stdClass $record * @param array $options * @return core_course_category course category record */ public function create_category($record=null, ?array $options=null) { $this->categorycount++; $i = $this->categorycount; $record = (array)$record; if (!isset($record['name'])) { $record['name'] = 'Course category '.$i; } if (!isset($record['description'])) { $record['description'] = "Test course category $i\n$this->loremipsum"; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } return core_course_category::create($record); } /** * Create test cohort. * @param array|stdClass $record * @param array $options * @return stdClass cohort record */ public function create_cohort($record=null, ?array $options=null) { global $DB, $CFG; require_once("$CFG->dirroot/cohort/lib.php"); $this->cohortcount++; $i = $this->cohortcount; $record = (array)$record; if (!isset($record['contextid'])) { $record['contextid'] = context_system::instance()->id; } if (!isset($record['name'])) { $record['name'] = 'Cohort '.$i; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } if (!isset($record['description'])) { $record['description'] = "Description for '{$record['name']}' \n$this->loremipsum"; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } if (!isset($record['visible'])) { $record['visible'] = 1; } if (!isset($record['component'])) { $record['component'] = ''; } $id = cohort_add_cohort((object)$record); return $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST); } /** * Create a test course * @param array|stdClass $record Apart from the course information, the following can be also set: * 'initsections' => bool for section name initialization, renaming them to "Section X". Default value is 0 (false). * @param array $options with keys: * 'createsections' => bool precreate all sections * @return stdClass course record */ public function create_course($record=null, ?array $options=null) { global $DB, $CFG; require_once("$CFG->dirroot/course/lib.php"); $this->coursecount++; $i = $this->coursecount; $record = (array)$record; if (!isset($record['fullname'])) { $record['fullname'] = 'Test course '.$i; } if (!isset($record['shortname'])) { $record['shortname'] = 'tc_'.$i; } if (!isset($record['idnumber'])) { $record['idnumber'] = ''; } if (!isset($record['format'])) { $record['format'] = 'topics'; } if (!isset($record['newsitems'])) { $record['newsitems'] = 0; } if (!isset($record['numsections'])) { $record['numsections'] = 5; } if (!isset($record['summary'])) { $record['summary'] = "Test course $i\n$this->loremipsum"; } if (!isset($record['summaryformat'])) { $record['summaryformat'] = FORMAT_MOODLE; } if (!isset($record['category'])) { $record['category'] = $DB->get_field_select('course_categories', "MIN(id)", "parent=0"); } if (!isset($record['startdate'])) { $record['startdate'] = usergetmidnight(time()); } if (isset($record['tags']) && !is_array($record['tags'])) { $record['tags'] = preg_split('/\s*,\s*/', trim($record['tags']), -1, PREG_SPLIT_NO_EMPTY); } if (!empty($options['createsections']) && empty($record['numsections'])) { // Since Moodle 3.3 function create_course() automatically creates sections if numsections is specified. // For BC if 'createsections' is given but 'numsections' is not, assume the default value from config. $record['numsections'] = get_config('moodlecourse', 'numsections'); } if (!empty($record['customfields'])) { foreach ($record['customfields'] as $field) { $record['customfield_'.$field['shortname']] = $field['value']; } } $initsections = !empty($record['initsections']); unset($record['initsections']); $course = create_course((object)$record); if ($initsections) { $this->init_sections($course); } context_course::instance($course->id); return $course; } /** * Initializes sections for a specified course, such as configuring section names for courses using 'Section X'. * * @param stdClass $course The course object. */ private function init_sections(stdClass $course): void { global $DB; $sections = $DB->get_records('course_sections', ['course' => $course->id], 'section'); foreach ($sections as $section) { if ($section->section != 0) { $DB->set_field( table: 'course_sections', newfield: 'name', newvalue: get_string('section', 'core') . ' ' . $section->section, conditions: [ 'id' => $section->id, ], ); } } } /** * Create course section if does not exist yet * @param array|stdClass $record must contain 'course' and 'section' attributes * @param array|null $options * @return section_info * @throws coding_exception */ public function create_course_section($record = null, ?array $options = null) { global $DB; $record = (array)$record; if (empty($record['course'])) { throw new coding_exception('course must be present in testing_data_generator::create_course_section() $record'); } if (!isset($record['section'])) { throw new coding_exception('section must be present in testing_data_generator::create_course_section() $record'); } course_create_sections_if_missing($record['course'], $record['section']); return get_fast_modinfo($record['course'])->get_section_info($record['section']); } /** * Create a test block. * * The $record passed in becomes the basis for the new row added to the * block_instances table. You only need to supply the values of interest. * Any missing values have sensible defaults filled in, and ->blockname will be set based on $blockname. * * The $options array provides additional data, not directly related to what * will be inserted in the block_instance table, which may affect the block * that is created. The meanings of any data passed here depends on the particular * type of block being created. * * @param string $blockname the type of block to create. E.g. 'html'. * @param array|stdClass $record forms the basis for the entry to be inserted in the block_instances table. * @param array $options further, block-specific options to control how the block is created. * @return stdClass new block_instance record. */ public function create_block($blockname, $record=null, array $options=array()) { $generator = $this->get_plugin_generator('block_'.$blockname); return $generator->create_instance($record, $options); } /** * Create a test activity module. * * The $record should contain the same data that you would call from * ->get_data() when the mod_[type]_mod_form is submitted, except that you * only need to supply values of interest. The only required value is * 'course'. Any missing values will have a sensible default supplied. * * The $options array provides additional data, not directly related to what * would come back from the module edit settings form, which may affect the activity * that is created. The meanings of any data passed here depends on the particular * type of activity being created. * * @param string $modulename the type of activity to create. E.g. 'forum' or 'quiz'. * @param array|stdClass $record data, as if from the module edit settings form. * @param array $options additional data that may affect how the module is created. * @return stdClass activity record new new record that was just inserted in the table * like 'forum' or 'quiz', with a ->cmid field added. */ public function create_module($modulename, $record=null, ?array $options=null) { $generator = $this->get_plugin_generator('mod_'.$modulename); return $generator->create_instance($record, $options); } /** * Create a test group for the specified course * * $record should be either an array or a stdClass containing infomation about the group to create. * At the very least it needs to contain courseid. * Default values are added for name, description, and descriptionformat if they are not present. * * This function calls groups_create_group() to create the group within the database. * @see groups_create_group * @param array|stdClass $record * @return stdClass group record */ public function create_group($record) { global $DB, $CFG; require_once($CFG->dirroot . '/group/lib.php'); $this->groupcount++; $i = str_pad($this->groupcount, 4, '0', STR_PAD_LEFT); $record = (array)$record; if (empty($record['courseid'])) { throw new coding_exception('courseid must be present in testing_data_generator::create_group() $record'); } if (!isset($record['name'])) { $record['name'] = 'group-' . $i; } if (!isset($record['description'])) { $record['description'] = "Test Group $i\n{$this->loremipsum}"; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } if (!isset($record['visibility'])) { $record['visibility'] = GROUPS_VISIBILITY_ALL; } if (!isset($record['participation'])) { $record['participation'] = true; } $id = groups_create_group((object)$record); // Allow tests to set group pictures. if (!empty($record['picturepath'])) { require_once($CFG->dirroot . '/lib/gdlib.php'); $grouppicture = process_new_icon(\context_course::instance($record['courseid']), 'group', 'icon', $id, $record['picturepath']); $DB->set_field('groups', 'picture', $grouppicture, ['id' => $id]); // Invalidate the group data as we've updated the group record. cache_helper::invalidate_by_definition('core', 'groupdata', array(), [$record['courseid']]); } return $DB->get_record('groups', array('id'=>$id)); } /** * Create a test group member * @param array|stdClass $record * @throws coding_exception * @return boolean */ public function create_group_member($record) { global $DB, $CFG; require_once($CFG->dirroot . '/group/lib.php'); $record = (array)$record; if (empty($record['userid'])) { throw new coding_exception('user must be present in testing_util::create_group_member() $record'); } if (!isset($record['groupid'])) { throw new coding_exception('group must be present in testing_util::create_group_member() $record'); } if (!isset($record['component'])) { $record['component'] = null; } if (!isset($record['itemid'])) { $record['itemid'] = 0; } return groups_add_member($record['groupid'], $record['userid'], $record['component'], $record['itemid']); } /** * Create a test grouping for the specified course * * $record should be either an array or a stdClass containing infomation about the grouping to create. * At the very least it needs to contain courseid. * Default values are added for name, description, and descriptionformat if they are not present. * * This function calls groups_create_grouping() to create the grouping within the database. * @see groups_create_grouping * @param array|stdClass $record * @return stdClass grouping record */ public function create_grouping($record) { global $DB, $CFG; require_once($CFG->dirroot . '/group/lib.php'); $this->groupingcount++; $i = $this->groupingcount; $record = (array)$record; if (empty($record['courseid'])) { throw new coding_exception('courseid must be present in testing_data_generator::create_grouping() $record'); } if (!isset($record['name'])) { $record['name'] = 'grouping-' . $i; } if (!isset($record['description'])) { $record['description'] = "Test Grouping $i\n{$this->loremipsum}"; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } $id = groups_create_grouping((object)$record); return $DB->get_record('groupings', array('id'=>$id)); } /** * Create a test grouping group * @param array|stdClass $record * @throws coding_exception * @return boolean */ public function create_grouping_group($record) { global $DB, $CFG; require_once($CFG->dirroot . '/group/lib.php'); $record = (array)$record; if (empty($record['groupingid'])) { throw new coding_exception('grouping must be present in testing::create_grouping_group() $record'); } if (!isset($record['groupid'])) { throw new coding_exception('group must be present in testing_util::create_grouping_group() $record'); } return groups_assign_grouping($record['groupingid'], $record['groupid']); } /** * Create an instance of a repository. * * @param string type of repository to create an instance for. * @param array|stdClass $record data to use to up set the instance. * @param array $options options * @return stdClass repository instance record * @since Moodle 2.5.1 */ public function create_repository($type, $record=null, ?array $options = null) { $generator = $this->get_plugin_generator('repository_'.$type); return $generator->create_instance($record, $options); } /** * Create an instance of a repository. * * @param string type of repository to create an instance for. * @param array|stdClass $record data to use to up set the instance. * @param array $options options * @return repository_type object * @since Moodle 2.5.1 */ public function create_repository_type($type, $record=null, ?array $options = null) { $generator = $this->get_plugin_generator('repository_'.$type); return $generator->create_type($record, $options); } /** * Create a test scale * @param array|stdClass $record * @param array $options * @return stdClass block instance record */ public function create_scale($record=null, ?array $options=null) { global $DB; $this->scalecount++; $i = $this->scalecount; $record = (array)$record; if (!isset($record['name'])) { $record['name'] = 'Test scale '.$i; } if (!isset($record['scale'])) { $record['scale'] = 'A,B,C,D,F'; } if (!isset($record['courseid'])) { $record['courseid'] = 0; } if (!isset($record['userid'])) { $record['userid'] = 0; } if (!isset($record['description'])) { $record['description'] = 'Test scale description '.$i; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } $record['timemodified'] = time(); if (isset($record['id'])) { $DB->import_record('scale', $record); $DB->get_manager()->reset_sequence('scale'); $id = $record['id']; } else { $id = $DB->insert_record('scale', $record); } return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST); } /** * Creates a new role in the system. * * You can fill $record with the role 'name', * 'shortname', 'description' and 'archetype'. * * If an archetype is specified it's capabilities, * context where the role can be assigned and * all other properties are copied from the archetype; * if no archetype is specified it will create an * empty role. * * @param array|stdClass $record * @return int The new role id */ public function create_role($record=null) { global $DB; $this->rolecount++; $i = $this->rolecount; $record = (array)$record; if (empty($record['shortname'])) { $record['shortname'] = 'role-' . $i; } if (empty($record['name'])) { $record['name'] = 'Test role ' . $i; } if (empty($record['description'])) { $record['description'] = 'Test role ' . $i . ' description'; } if (empty($record['archetype'])) { $record['archetype'] = ''; } else { $archetypes = get_role_archetypes(); if (empty($archetypes[$record['archetype']])) { throw new coding_exception('\'role\' requires the field \'archetype\' to specify a ' . 'valid archetype shortname (editingteacher, student...)'); } } // Creates the role. if (!$newroleid = create_role($record['name'], $record['shortname'], $record['description'], $record['archetype'])) { throw new coding_exception('There was an error creating \'' . $record['shortname'] . '\' role'); } // If no archetype was specified we allow it to be added to all contexts, // otherwise we allow it in the archetype contexts. if (!$record['archetype']) { $contextlevels = []; $usefallback = true; foreach (context_helper::get_all_levels() as $level => $title) { if (array_key_exists($title, $record)) { $usefallback = false; if (!empty($record[$title])) { $contextlevels[] = $level; } } } if ($usefallback) { $contextlevels = array_keys(context_helper::get_all_levels()); } } else { // Copying from the archetype default rol. $archetyperoleid = $DB->get_field( 'role', 'id', array('shortname' => $record['archetype'], 'archetype' => $record['archetype']) ); $contextlevels = get_role_contextlevels($archetyperoleid); } set_role_contextlevels($newroleid, $contextlevels); if ($record['archetype']) { // We copy all the roles the archetype can assign, override, switch to and view. if ($record['archetype']) { $types = array('assign', 'override', 'switch', 'view'); foreach ($types as $type) { $rolestocopy = get_default_role_archetype_allows($type, $record['archetype']); foreach ($rolestocopy as $tocopy) { $functionname = "core_role_set_{$type}_allowed"; $functionname($newroleid, $tocopy); } } } // Copying the archetype capabilities. $sourcerole = $DB->get_record('role', array('id' => $archetyperoleid)); role_cap_duplicate($sourcerole, $newroleid); } $allcapabilities = get_all_capabilities(); $foundcapabilities = array_intersect(array_keys($allcapabilities), array_keys($record)); $systemcontext = \context_system::instance(); $allpermissions = [ 'inherit' => CAP_INHERIT, 'allow' => CAP_ALLOW, 'prevent' => CAP_PREVENT, 'prohibit' => CAP_PROHIBIT, ]; foreach ($foundcapabilities as $capability) { $permission = $record[$capability]; if (!array_key_exists($permission, $allpermissions)) { throw new \coding_exception("Unknown capability permissions '{$permission}'"); } assign_capability( $capability, $allpermissions[$permission], $newroleid, $systemcontext->id, true ); } return $newroleid; } /** * Set role capabilities for the specified role. * * @param int $roleid The Role to set capabilities for * @param array $rolecapabilities The list of capability =>permission to set for this role * @param null|context $context The context to apply this capability to */ public function create_role_capability(int $roleid, array $rolecapabilities, ?context $context = null): void { // Map the capabilities into human-readable names. $allpermissions = [ 'inherit' => CAP_INHERIT, 'allow' => CAP_ALLOW, 'prevent' => CAP_PREVENT, 'prohibit' => CAP_PROHIBIT, ]; // Fetch all capabilities to check that they exist. $allcapabilities = get_all_capabilities(); foreach ($rolecapabilities as $capability => $permission) { if ($permission === '') { // Allow items to be skipped. continue; } if (!array_key_exists($capability, $allcapabilities)) { throw new \coding_exception("Unknown capability '{$capability}'"); } if (!array_key_exists($permission, $allpermissions)) { throw new \coding_exception("Unknown capability permissions '{$permission}'"); } assign_capability( $capability, $allpermissions[$permission], $roleid, $context->id, true ); } } /** * Create a tag. * * @param array|stdClass $record * @return stdClass the tag record */ public function create_tag($record = null) { global $DB, $USER; $this->tagcount++; $i = $this->tagcount; $record = (array) $record; if (!isset($record['userid'])) { $record['userid'] = $USER->id; } if (!isset($record['rawname'])) { if (isset($record['name'])) { $record['rawname'] = $record['name']; } else { $record['rawname'] = 'Tag name ' . $i; } } // Attribute 'name' should be a lowercase version of 'rawname', if not set. if (!isset($record['name'])) { $record['name'] = core_text::strtolower($record['rawname']); } else { $record['name'] = core_text::strtolower($record['name']); } if (!isset($record['tagcollid'])) { $record['tagcollid'] = core_tag_collection::get_default(); } if (!isset($record['description'])) { $record['description'] = 'Tag description'; } if (!isset($record['descriptionformat'])) { $record['descriptionformat'] = FORMAT_MOODLE; } if (!isset($record['flag'])) { $record['flag'] = 0; } if (!isset($record['timemodified'])) { $record['timemodified'] = time(); } $id = $DB->insert_record('tag', $record); return $DB->get_record('tag', array('id' => $id), '*', MUST_EXIST); } /** * Helper method which combines $defaults with the values specified in $record. * If $record is an object, it is converted to an array. * Then, for each key that is in $defaults, but not in $record, the value * from $defaults is copied. * @param array $defaults the default value for each field with * @param array|stdClass $record * @return array updated $record. */ public function combine_defaults_and_record(array $defaults, $record) { $record = (array) $record; foreach ($defaults as $key => $default) { if (!array_key_exists($key, $record)) { $record[$key] = $default; } else if (is_array($record[$key]) && is_array($default)) { $record[$key] = $this->combine_defaults_and_record($default, $record[$key]); } } return $record; } /** * Simplified enrolment of user to course using default options. * * It is strongly recommended to use only this method for 'manual' and 'self' plugins only!!! * * @param int $userid * @param int $courseid * @param int|string $roleidorshortname optional role id or role shortname, use only with manual plugin * @param string $enrol name of enrol plugin, * there must be exactly one instance in course, * it must support enrol_user() method. * @param int $timestart (optional) 0 means unknown * @param int $timeend (optional) 0 means forever * @param int $status (optional) default to ENROL_USER_ACTIVE for new enrolments * @return bool success */ public function enrol_user($userid, $courseid, $roleidorshortname = null, $enrol = 'manual', $timestart = 0, $timeend = 0, $status = null) { global $DB; // If role is specified by shortname, convert it into an id. if (!is_numeric($roleidorshortname) && is_string($roleidorshortname)) { $roleid = $DB->get_field('role', 'id', array('shortname' => $roleidorshortname), MUST_EXIST); } else { $roleid = $roleidorshortname; } if (!$plugin = enrol_get_plugin($enrol)) { return false; } $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'enrol'=>$enrol)); if (count($instances) != 1) { return false; } $instance = reset($instances); if (is_null($roleid) and $instance->roleid) { $roleid = $instance->roleid; } $plugin->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status); return true; } /** * Assigns the specified role to a user in the context. * * @param int|string $role either an int role id or a string role shortname. * @param int $userid * @param int|context $contextid Defaults to the system context * @return int new/existing id of the assignment */ public function role_assign($role, $userid, $contextid = false) { global $DB; // Default to the system context. if (!$contextid) { $context = context_system::instance(); $contextid = $context->id; } if (empty($role)) { throw new coding_exception('roleid must be present in testing_data_generator::role_assign() arguments'); } if (!is_number($role)) { $role = $DB->get_field('role', 'id', ['shortname' => $role], MUST_EXIST); } if (empty($userid)) { throw new coding_exception('userid must be present in testing_data_generator::role_assign() arguments'); } return role_assign($role, $userid, $contextid); } /** * Create a grade_category. * * @param array|stdClass $record * @return stdClass the grade category record */ public function create_grade_category($record = null) { global $CFG; $this->gradecategorycounter++; $record = (array)$record; if (empty($record['courseid'])) { throw new coding_exception('courseid must be present in testing::create_grade_category() $record'); } if (!isset($record['fullname'])) { $record['fullname'] = 'Grade category ' . $this->gradecategorycounter; } // For gradelib classes. require_once($CFG->libdir . '/gradelib.php'); // Create new grading category in this course. $gradecategory = new grade_category(array('courseid' => $record['courseid']), false); $gradecategory->apply_default_settings(); grade_category::set_properties($gradecategory, $record); $gradecategory->apply_forced_settings(); $gradecategory->insert(); // This creates a default grade item for the category $gradeitem = $gradecategory->load_grade_item(); $gradecategory->update_from_db(); return $gradecategory->get_record_data(); } /** * Create a grade_grade. * * @param array $record * @return grade_grade the grade record */ public function create_grade_grade(?array $record = null): grade_grade { global $DB, $USER; $item = $DB->get_record('grade_items', ['id' => $record['itemid']]); $userid = $record['userid'] ?? $USER->id; unset($record['itemid']); unset($record['userid']); if ($item->itemtype === 'mod') { $cm = get_coursemodule_from_instance($item->itemmodule, $item->iteminstance); $module = new $item->itemmodule(context_module::instance($cm->id), $cm, false); $record['attemptnumber'] = $record['attemptnumber'] ?? 0; $module->save_grade($userid, (object) $record); $grade = grade_grade::fetch(['userid' => $userid, 'itemid' => $item->id]); } else { $grade = grade_grade::fetch(['userid' => $userid, 'itemid' => $item->id]); $record['rawgrade'] = $record['rawgrade'] ?? $record['grade'] ?? null; $record['finalgrade'] = $record['finalgrade'] ?? $record['grade'] ?? null; unset($record['grade']); if ($grade) { $fields = $grade->required_fields + array_keys($grade->optional_fields); foreach ($fields as $field) { $grade->{$field} = $record[$field] ?? $grade->{$field}; } $grade->update(); } else { $record['userid'] = $userid; $record['itemid'] = $item->id; $grade = new grade_grade($record, false); $grade->insert(); } } return $grade; } /** * Create a grade_item. * * @param array|stdClass $record * @return stdClass the grade item record */ public function create_grade_item($record = null) { global $CFG; require_once("$CFG->libdir/gradelib.php"); $this->gradeitemcounter++; if (!isset($record['itemtype'])) { $record['itemtype'] = 'manual'; } if (!isset($record['itemname'])) { $record['itemname'] = 'Grade item ' . $this->gradeitemcounter; } if (isset($record['outcomeid'])) { $outcome = new grade_outcome(array('id' => $record['outcomeid'])); $record['scaleid'] = $outcome->scaleid; } if (isset($record['scaleid'])) { $record['gradetype'] = GRADE_TYPE_SCALE; } else if (!isset($record['gradetype'])) { $record['gradetype'] = GRADE_TYPE_VALUE; } // Create new grade item in this course. $gradeitem = new grade_item($record, false); $gradeitem->insert(); $gradeitem->update_from_db(); return $gradeitem->get_record_data(); } /** * Create a grade_outcome. * * @param array|stdClass $record * @return stdClass the grade outcome record */ public function create_grade_outcome($record = null) { global $CFG; $this->gradeoutcomecounter++; $i = $this->gradeoutcomecounter; if (!isset($record['fullname'])) { $record['fullname'] = 'Grade outcome ' . $i; } // For gradelib classes. require_once($CFG->libdir . '/gradelib.php'); // Create new grading outcome in this course. $gradeoutcome = new grade_outcome($record, false); $gradeoutcome->insert(); $gradeoutcome->update_from_db(); return $gradeoutcome->get_record_data(); } /** * Helper function used to create an LTI tool. * * @param stdClass $data * @return stdClass the tool */ public function create_lti_tool($data = array()) { global $DB; $studentrole = $DB->get_record('role', array('shortname' => 'student')); $teacherrole = $DB->get_record('role', array('shortname' => 'teacher')); // Create a course if no course id was specified. if (empty($data->courseid)) { $course = $this->create_course(); $data->courseid = $course->id; } else { $course = get_course($data->courseid); } if (!empty($data->cmid)) { $data->contextid = context_module::instance($data->cmid)->id; } else { $data->contextid = context_course::instance($data->courseid)->id; } // Set it to enabled if no status was specified. if (!isset($data->status)) { $data->status = ENROL_INSTANCE_ENABLED; } // Default to legacy lti version. if (empty($data->ltiversion) || !in_array($data->ltiversion, ['LTI-1p0/LTI-2p0', 'LTI-1p3'])) { $data->ltiversion = 'LTI-1p0/LTI-2p0'; } // Add some extra necessary fields to the data. $data->name = $data->name ?? 'Test LTI'; $data->roleinstructor = $teacherrole->id; $data->rolelearner = $studentrole->id; // Get the enrol LTI plugin. $enrolplugin = enrol_get_plugin('lti'); $instanceid = $enrolplugin->add_instance($course, (array) $data); // Get the tool associated with this instance. return $DB->get_record('enrol_lti_tools', array('enrolid' => $instanceid)); } /** * Helper function used to create an event. * * @param array $data * @return stdClass */ public function create_event($data = []) { global $CFG; require_once($CFG->dirroot . '/calendar/lib.php'); $record = new \stdClass(); $record->name = 'event name'; $record->repeat = 0; $record->repeats = 0; $record->timestart = time(); $record->timeduration = 0; $record->timesort = 0; $record->eventtype = 'user'; $record->courseid = 0; $record->categoryid = 0; foreach ($data as $key => $value) { $record->$key = $value; } switch ($record->eventtype) { case 'user': unset($record->categoryid); unset($record->courseid); unset($record->groupid); break; case 'group': unset($record->categoryid); break; case 'course': unset($record->categoryid); unset($record->groupid); break; case 'category': unset($record->courseid); unset($record->groupid); break; case 'site': unset($record->categoryid); unset($record->courseid); unset($record->groupid); break; } $event = new calendar_event($record); $event->create($record); return $event->properties(); } /** * Create a new course custom field category with the given name. * * @param array $data Array with data['name'] of category * @return \core_customfield\category_controller The created category */ public function create_custom_field_category($data): \core_customfield\category_controller { return $this->get_plugin_generator('core_customfield')->create_category($data); } /** * Create a new custom field * * @param array $data Array with 'name', 'shortname' and 'type' of the field * @return \core_customfield\field_controller The created field */ public function create_custom_field($data): \core_customfield\field_controller { global $DB; if (empty($data['categoryid']) && !empty($data['category'])) { $data['categoryid'] = $DB->get_field('customfield_category', 'id', ['name' => $data['category']]); unset($data['category']); } return $this->get_plugin_generator('core_customfield')->create_field($data); } /** * Create a new category for custom profile fields. * * @param array $data Array with 'name' and optionally 'sortorder' * @return \stdClass New category object */ public function create_custom_profile_field_category(array $data): \stdClass { global $DB; // Pick next sortorder if not defined. if (!array_key_exists('sortorder', $data)) { $data['sortorder'] = (int)$DB->get_field_sql('SELECT MAX(sortorder) FROM {user_info_category}') + 1; } $category = (object)[ 'name' => $data['name'], 'sortorder' => $data['sortorder'] ]; $category->id = $DB->insert_record('user_info_category', $category); return $category; } /** * Creates a new custom profile field. * * Optional fields are: * * categoryid (or use 'category' to specify by name). If you don't specify * either, it will add the field to a 'Testing' category, which will be created for you if * necessary. * * sortorder (if you don't specify this, it will pick the next one in the category). * * all the other database fields (if you don't specify this, it will pick sensible defaults * based on the data type). * * @param array $data Array with 'datatype', 'shortname', and 'name' * @return \stdClass Database object from the user_info_field table */ public function create_custom_profile_field(array $data): \stdClass { global $DB, $CFG; require_once($CFG->dirroot . '/user/profile/lib.php'); // Set up category if necessary. if (!array_key_exists('categoryid', $data)) { if (array_key_exists('category', $data)) { $data['categoryid'] = $DB->get_field('user_info_category', 'id', ['name' => $data['category']], MUST_EXIST); } else { // Make up a 'Testing' category or use existing. $data['categoryid'] = $DB->get_field('user_info_category', 'id', ['name' => 'Testing']); if (!$data['categoryid']) { $created = $this->create_custom_profile_field_category(['name' => 'Testing']); $data['categoryid'] = $created->id; } } } // Pick sort order if necessary. if (!array_key_exists('sortorder', $data)) { $data['sortorder'] = (int)$DB->get_field_sql( 'SELECT MAX(sortorder) FROM {user_info_field} WHERE categoryid = ?', [$data['categoryid']]) + 1; } if ($data['datatype'] === 'menu' && isset($data['param1'])) { // Convert new lines to the proper character. $data['param1'] = str_replace('\n', "\n", $data['param1']); } // Defaults for other values. $defaults = [ 'description' => '', 'descriptionformat' => 0, 'required' => 0, 'locked' => 0, 'visible' => PROFILE_VISIBLE_ALL, 'forceunique' => 0, 'signup' => 0, 'defaultdata' => '', 'defaultdataformat' => 0, 'param1' => '', 'param2' => '', 'param3' => '', 'param4' => '', 'param5' => '' ]; // Type-specific defaults for other values. $typedefaults = [ 'text' => [ 'param1' => 30, 'param2' => 2048 ], 'menu' => [ 'param1' => "Yes\nNo", 'defaultdata' => 'No' ], 'datetime' => [ 'param1' => '2010', 'param2' => '2015', 'param3' => 1 ], 'checkbox' => [ 'defaultdata' => 0 ] ]; foreach ($typedefaults[$data['datatype']] ?? [] as $field => $value) { $defaults[$field] = $value; } foreach ($defaults as $field => $value) { if (!array_key_exists($field, $data)) { $data[$field] = $value; } } $data['id'] = $DB->insert_record('user_info_field', $data); return (object)$data; } /** * Create a new user, and enrol them in the specified course as the supplied role. * * @param \stdClass $course The course to enrol in * @param string $role The role to give within the course * @param \stdClass|array $userparams User parameters * @return \stdClass The created user */ public function create_and_enrol($course, $role = 'student', $userparams = null, $enrol = 'manual', $timestart = 0, $timeend = 0, $status = null) { global $DB; $user = $this->create_user($userparams); $roleid = $DB->get_field('role', 'id', ['shortname' => $role ]); $this->enrol_user($user->id, $course->id, $roleid, $enrol, $timestart, $timeend, $status); return $user; } /** * Create a new last access record for a given user in a course. * * @param \stdClass $user The user * @param \stdClass $course The course the user accessed * @param int $timestamp The timestamp for when the user last accessed the course * @return \stdClass The user_lastaccess record */ public function create_user_course_lastaccess(\stdClass $user, \stdClass $course, int $timestamp): \stdClass { global $DB; $record = [ 'userid' => $user->id, 'courseid' => $course->id, 'timeaccess' => $timestamp, ]; $recordid = $DB->insert_record('user_lastaccess', $record); return $DB->get_record('user_lastaccess', ['id' => $recordid], '*', MUST_EXIST); } /** * Generate a stored_progress record and return the ID. * * All fields are optional, required fields will be generated if not supplied. * * @param ?string $idnumber The unique ID Number for this stored progress. * @param ?int $timestart The time progress was started, defaults to now. * @param ?int $lastupdate The time the progress was last updated. * @param float $percent The percentage progress so far. * @param ?string $message An error message. * @param ?bool $haserrored Whether the process has encountered an error. * @return stdClass The record including the inserted id. * @throws dml_exception */ public function create_stored_progress( ?string $idnumber = null, ?int $timestart = null, ?int $lastupdate = null, float $percent = 0.00, ?string $message = null, ?bool $haserrored = false, ): stdClass { global $DB; $record = (object)[ 'idnumber' => $idnumber ?? random_string(), 'timestart' => $timestart ?? time(), 'lastupdate' => $lastupdate, 'percentcompleted' => $percent, 'message' => $message, 'haserrored' => $haserrored, ]; $record->id = $DB->insert_record('stored_progress', $record); return $record; } /** * Generate a stored progress record from an array of fields. * * For use as a behat createable entity. Use {@see self::create_stored_progress()} if calling directly. * * @param array $data * @return void */ public function create_stored_progress_bar(array $data): void { $this->create_stored_progress( $data['idnumber'] ?? null, $data['timestart'] ?? null, $data['lastupdate'] ?? null, $data['percent'] ?? 0.00, $data['message'] ?? null, $data['haserrored'] ?? false, ); } /** * Gets a default generator for a given component. * * @param string $component The component name, e.g. 'mod_forum' or 'core_question'. * @param string $classname The name of the class missing from the generators file. * @return component_generator_base The generator. */ protected function get_default_plugin_generator(string $component, ?string $classname = null) { [$type, $plugin] = core_component::normalize_component($component); switch ($type) { case 'block': return new default_block_generator($this, $plugin); } if (is_null($classname)) { throw new coding_exception("Component {$component} does not support " . "generators yet. Missing tests/generator/lib.php."); } throw new coding_exception("Component {$component} does not support " . "data generators yet. Class {$classname} not found."); } } PK\ default_block_generator.phpnu[. /** * Default block generator class. * * @package core * @category test * @copyright 2021 Moodle Pty Ltd. (http://moodle.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Default block generator class to be used when a specific one is not supported. * * @package core * @category test * @copyright 2021 Moodle Pty Ltd. (http://moodle.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class default_block_generator extends testing_block_generator { /** * @var string */ private $blockname; /** * Constructor. * @param testing_data_generator $datagenerator * @param string $blockname */ public function __construct(testing_data_generator $datagenerator, string $blockname) { parent::__construct($datagenerator); $this->blockname = $blockname; } /** * {@inheritdoc} */ public function get_blockname() { return $this->blockname; } } PK\/bblock_generator.phpnu[. /** * Block generator base class. * * @package core * @category test * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Block generator base class. * * Extend in blocks/xxxx/tests/generator/lib.php as class block_xxxx_generator. * * @package core * @category test * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class testing_block_generator extends component_generator_base { /** @var int number of created instances */ protected $instancecount = 0; /** * To be called from data reset code only, * do not use in tests. * @return void */ public function reset() { $this->instancecount = 0; } /** * Returns block name * @return string name of block that this class describes * @throws coding_exception if class invalid */ public function get_blockname() { $matches = null; if (!preg_match('/^block_([a-z0-9_]+)_generator$/', get_class($this), $matches)) { throw new coding_exception('Invalid block generator class name: '.get_class($this)); } if (empty($matches[1])) { throw new coding_exception('Invalid block generator class name: '.get_class($this)); } return $matches[1]; } /** * Fill in record defaults. * * @param stdClass $record * @return stdClass */ protected function prepare_record(stdClass $record) { $record->blockname = $this->get_blockname(); if (!isset($record->parentcontextid)) { $record->parentcontextid = context_system::instance()->id; } if (!isset($record->showinsubcontexts)) { $record->showinsubcontexts = 0; } if (!isset($record->pagetypepattern)) { $record->pagetypepattern = '*'; } if (!isset($record->subpagepattern)) { $record->subpagepattern = null; } if (!isset($record->defaultregion)) { $record->defaultregion = 'side-pre'; } if (!isset($record->defaultweight)) { $record->defaultweight = 5; } if (!isset($record->configdata)) { $record->configdata = null; } return $record; } /** * Create a test block instance. * * The $record passed in becomes the basis for the new row added to the * block_instances table. You only need to supply the values of interest. * Any missing values have sensible defaults filled in. * * The $options array provides additional data, not directly related to what * will be inserted in the block_instance table, which may affect the block * that is created. The meanings of any data passed here depends on the particular * type of block being created. * * @param array|stdClass $record forms the basis for the entry to be inserted in the block_instances table. * @param array $options further, block-specific options to control how the block is created. * @return stdClass the block_instance record that has just been created. */ public function create_instance($record = null, $options = array()) { global $DB, $PAGE; $this->instancecount++; // Creating a block is a back end operation, which should not cause any output to happen. // This will allow us to check that the theme was not initialised while creating the block instance. $outputstartedbefore = $PAGE->get_where_theme_was_initialised(); $record = (object)(array)$record; $this->preprocess_record($record, $options); $record = $this->prepare_record($record); if (empty($record->timecreated)) { $record->timecreated = time(); } if (empty($record->timemodified)) { $record->timemodified = time(); } $id = $DB->insert_record('block_instances', $record); context_block::instance($id); $instance = $DB->get_record('block_instances', array('id' => $id), '*', MUST_EXIST); // If the theme was initialised while creating the block instance, something somewhere called an output // function. Rather than leaving this as a hard-to-debug situation, let's make it fail with a clear error. $outputstartedafter = $PAGE->get_where_theme_was_initialised(); if ($outputstartedbefore === null && $outputstartedafter !== null) { throw new coding_exception('Creating a block_' . $this->get_blockname() . ' initialised the theme and output!', 'This should not happen. Creating a block should be a pure back-end operation. Unnecessarily initialising ' . 'the output mechanism at the wrong time can cause subtle bugs and is a significant performance hit. There is ' . 'likely a call to an output function that caused it:' . PHP_EOL . PHP_EOL . format_backtrace($outputstartedafter, true)); } return $instance; } /** * Can be overridden to do block-specific processing. $record can be modified * in-place. * * @param stdClass $record the data, before defaults are filled in. * @param array $options further, block-specific options, as passed to {@link create_instance()}. */ protected function preprocess_record(stdClass $record, array $options) { } } PK\ψ 3434module_generator.phpnu[. /** * Module generator base class. * * @package core * @category test * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Module generator base class. * * Extend in mod/xxxx/tests/generator/lib.php as class mod_xxxx_generator. * * @package core * @category test * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class testing_module_generator extends component_generator_base { /** * @var int number of created instances */ protected $instancecount = 0; /** * To be called from data reset code only, * do not use in tests. * @return void */ public function reset() { $this->instancecount = 0; } /** * Returns module name * @return string name of module that this class describes * @throws coding_exception if class invalid */ public function get_modulename() { $matches = null; if (!preg_match('/^mod_([a-z0-9]+)_generator$/', get_class($this), $matches)) { throw new coding_exception('Invalid module generator class name: '.get_class($this)); } if (empty($matches[1])) { throw new coding_exception('Invalid module generator class name: '.get_class($this)); } return $matches[1]; } /** * Create course module and link it to course * * Since 2.6 it is recommended to use function add_moduleinfo() to create a module. * * @deprecated since 2.6 * @see testing_module_generator::create_instance() * * @param integer $courseid * @param array $options section, visible * @return integer $cm instance id */ protected function precreate_course_module($courseid, array $options) { global $DB, $CFG; require_once("$CFG->dirroot/course/lib.php"); $modulename = $this->get_modulename(); $sectionnum = isset($options['section']) ? $options['section'] : 0; unset($options['section']); // Prevent confusion, it would be overridden later in course_add_cm_to_section() anyway. $cm = new stdClass(); $cm->course = $courseid; $cm->module = $DB->get_field('modules', 'id', array('name'=>$modulename)); $cm->instance = 0; $cm->section = 0; $cm->idnumber = isset($options['idnumber']) ? $options['idnumber'] : 0; $cm->added = time(); $columns = $DB->get_columns('course_modules'); foreach ($options as $key => $value) { if ($key === 'id' or !isset($columns[$key])) { continue; } if (property_exists($cm, $key)) { continue; } $cm->$key = $value; } $cm->id = $DB->insert_record('course_modules', $cm); course_add_cm_to_section($courseid, $cm->id, $sectionnum, null, $modulename); return $cm->id; } /** * Called after *_add_instance() * * Since 2.6 it is recommended to use function add_moduleinfo() to create a module. * * @deprecated since 2.6 * @see testing_module_generator::create_instance() * * @param int $id * @param int $cmid * @return stdClass module instance */ protected function post_add_instance($id, $cmid) { global $DB; $DB->set_field('course_modules', 'instance', $id, array('id'=>$cmid)); $instance = $DB->get_record($this->get_modulename(), array('id'=>$id), '*', MUST_EXIST); $cm = get_coursemodule_from_id($this->get_modulename(), $cmid, $instance->course, true, MUST_EXIST); context_module::instance($cm->id); $instance->cmid = $cm->id; return $instance; } /** * Merges together arguments $record and $options and fills default module * fields that are shared by all module types * * @param object|array $record fields (different from defaults) for this module * @param null|array $options for backward-compatibility this may include fields from course_modules * table. They are merged into $record * @throws coding_exception if $record->course is not specified */ protected function prepare_moduleinfo_record($record, $options) { global $DB; // Make sure we don't modify the original object. $moduleinfo = (object)(array)$record; if (empty($moduleinfo->course)) { throw new coding_exception('module generator requires $record->course'); } $moduleinfo->modulename = $this->get_modulename(); $moduleinfo->module = $DB->get_field('modules', 'id', array('name' => $moduleinfo->modulename)); // Allow idnumber to be set as either $options['idnumber'] or $moduleinfo->cmidnumber or $moduleinfo->idnumber. // The actual field name is 'idnumber' but add_moduleinfo() expects 'cmidnumber'. if (isset($options['idnumber'])) { $moduleinfo->cmidnumber = $options['idnumber']; } else if (!isset($moduleinfo->cmidnumber) && isset($moduleinfo->idnumber)) { $moduleinfo->cmidnumber = $moduleinfo->idnumber; } // These are the fields from table 'course_modules' in 2.6 when the second // argument $options is being deprecated. // List excludes fields: instance (does not exist yet), course, module and idnumber (set above) $easymergefields = array('section', 'added', 'score', 'indent', 'visible', 'visibleold', 'groupmode', 'groupingid', 'completion', 'completiongradeitemnumber', 'completionview', 'completionexpected', 'completionpassgrade', 'availability', 'showdescription'); foreach ($easymergefields as $key) { if (isset($options[$key])) { $moduleinfo->$key = $options[$key]; } } // Set default values. Note that visibleold and completiongradeitemnumber are not used when creating a module. $defaults = array( 'section' => 0, 'visible' => 1, 'visibleoncoursepage' => 1, 'cmidnumber' => '', 'groupmode' => 0, 'groupingid' => 0, 'availability' => null, 'completion' => 0, 'completionview' => 0, 'completionexpected' => 0, 'completionpassgrade' => 0, 'conditiongradegroup' => array(), 'conditionfieldgroup' => array(), 'conditioncompletiongroup' => array() ); foreach ($defaults as $key => $value) { if (!isset($moduleinfo->$key)) { $moduleinfo->$key = $value; } } return $moduleinfo; } /** * Creates an instance of the module for testing purposes. * * Module type will be taken from the class name. Each module type may overwrite * this function to add other default values used by it. * * @param array|stdClass $record data for module being generated. Requires 'course' key * (an id or the full object). Also can have any fields from add module form. * @param null|array $options general options for course module. Since 2.6 it is * possible to omit this argument by merging options into $record * @return stdClass record from module-defined table with additional field * cmid (corresponding id in course_modules table) */ public function create_instance($record = null, ?array $options = null) { global $CFG, $DB, $PAGE; require_once($CFG->dirroot.'/course/modlib.php'); $this->instancecount++; // Creating an activity is a back end operation, which should not cause any output to happen. // This will allow us to check that the theme was not initialised while creating the module instance. $outputstartedbefore = $PAGE->get_where_theme_was_initialised(); // Merge options into record and add default values. $record = $this->prepare_moduleinfo_record($record, $options); // Retrieve the course record. if (!empty($record->course->id)) { $course = $record->course; $record->course = $record->course->id; } else { $course = get_course($record->course); } // Fill the name and intro with default values (if missing). if (empty($record->name)) { $record->name = get_string('pluginname', $this->get_modulename()).' '.$this->instancecount; // Module label can be created without name specified. It will get its name from the intro's text. if ($this->get_modulename() === 'label') { $record->name = ''; } } if (empty($record->introeditor) && empty($record->intro)) { $record->intro = 'Test '.$this->get_modulename().' ' . $this->instancecount; } if (empty($record->introeditor) && empty($record->introformat)) { $record->introformat = FORMAT_MOODLE; } if (isset($record->tags) && !is_array($record->tags)) { $record->tags = preg_split('/\s*,\s*/', trim($record->tags), -1, PREG_SPLIT_NO_EMPTY); } // Before Moodle 2.6 it was possible to create a module with completion tracking when // it is not setup for course and/or site-wide. Display debugging message so it is // easier to trace an error in unittests. if ($record->completion && empty($CFG->enablecompletion)) { debugging('Did you forget to set $CFG->enablecompletion before generating module with completion tracking?', DEBUG_DEVELOPER); } if ($record->completion && empty($course->enablecompletion)) { debugging('Did you forget to enable completion tracking for the course before generating module with completion tracking?', DEBUG_DEVELOPER); } if (!empty($record->lang) && !has_capability('moodle/course:setforcedlanguage', context_course::instance($course->id))) { throw new coding_exception('Attempt to generate an activity when the current user does not have ' . 'permission moodle/course:setforcedlanguage. This does not work.'); } // Add the module to the course. $moduleinfo = add_moduleinfo($record, $course); // Prepare object to return with additional field cmid. $modulename = $this->get_modulename(); $instance = $DB->get_record($modulename, ['id' => $moduleinfo->instance], '*', MUST_EXIST); $instance->cmid = $moduleinfo->coursemodule; // Insert files for the 'intro' file area. $instance = $this->insert_files( $instance, $record, $modulename, \context_module::instance($instance->cmid), "mod_{$modulename}", 'intro', 0 ); // If the theme was initialised while creating the module instance, something somewhere called an output // function. Rather than leaving this as a hard-to-debug situation, let's make it fail with a clear error. $outputstartedafter = $PAGE->get_where_theme_was_initialised(); if ($outputstartedbefore === null && $outputstartedafter !== null) { throw new coding_exception('Creating a mod_' . $this->get_modulename() . ' activity initialised the theme and output!', 'This should not happen. Creating an activity should be a pure back-end operation. Unnecessarily initialising ' . 'the output mechanism at the wrong time can cause subtle bugs and is a significant performance hit. There is ' . 'likely a call to an output function that caused it:' . PHP_EOL . PHP_EOL . format_backtrace($outputstartedafter, true)); } return $instance; } /** * Generates a piece of content for the module. * User is usually taken from global $USER variable. * @param stdClass $instance object returned from create_instance() call * @param stdClass|array $record * @return stdClass generated object * @throws coding_exception if function is not implemented by module */ public function create_content($instance, $record = array()) { throw new coding_exception('Module generator for '.$this->get_modulename().' does not implement method create_content()'); } } PK\̇repository_generator.phpnu[. /** * Repository data generator * * @package repository * @category test * @copyright 2013 Frédéric Massart * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Repository data generator class * * @package core * @category test * @copyright 2013 Frédéric Massart * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.5.1 */ class testing_repository_generator extends component_generator_base { /** * Number of instances created * @var int */ protected $instancecount = 0; /** * To be called from data reset code only, * do not use in tests. * @return void */ public function reset() { $this->instancecount = 0; } /** * Returns repository type name * * @return string name of the type of repository * @throws coding_exception if class invalid */ public function get_typename() { $matches = null; if (!preg_match('/^repository_([a-z0-9_]+)_generator$/', get_class($this), $matches)) { throw new coding_exception('Invalid repository generator class name: '.get_class($this)); } if (empty($matches[1])) { throw new coding_exception('Invalid repository generator class name: '.get_class($this)); } return $matches[1]; } /** * Fill in record defaults. * * @param array $record * @return array */ protected function prepare_record(array $record) { if (!isset($record['name'])) { $record['name'] = $this->get_typename() . ' ' . $this->instancecount; } if (!isset($record['contextid'])) { $record['contextid'] = context_system::instance()->id; } return $record; } /** * Fill in type record defaults. * * @param array $record * @return array */ protected function prepare_type_record(array $record) { if (!isset($record['pluginname'])) { $record['pluginname'] = ''; } if (!isset($record['enableuserinstances'])) { $record['enableuserinstances'] = 1; } if (!isset($record['enablecourseinstances'])) { $record['enablecourseinstances'] = 1; } return $record; } /** * Create a test repository instance. * * @param array|stdClass $record * @param array $options * @return stdClass repository instance record */ public function create_instance($record = null, ?array $options = null) { global $CFG, $DB, $PAGE; require_once($CFG->dirroot . '/repository/lib.php'); $this->instancecount++; $record = (array) $record; // Creating a repository is a back end operation, which should not cause any output to happen. // This will allow us to check that the theme was not initialised while creating the repository instance. $outputstartedbefore = $PAGE->get_where_theme_was_initialised(); $typeid = $DB->get_field('repository', 'id', array('type' => $this->get_typename()), MUST_EXIST); $instanceoptions = repository::static_function($this->get_typename(), 'get_instance_option_names'); if (empty($instanceoptions)) { // There can only be one instance of this repository, and it should have been created along with the type. $id = $DB->get_field('repository_instances', 'id', array('typeid' => $typeid), MUST_EXIST); } else { // Create the new instance, but first make sure all the required parameters are set. $record = $this->prepare_record($record); if (empty($record['contextid'])) { throw new coding_exception('contextid must be present in testing_repository_generator::create_instance() $record'); } foreach ($instanceoptions as $option) { if (!isset($record[$option])) { throw new coding_exception("$option must be present in testing_repository_generator::create_instance() \$record"); } } $context = context::instance_by_id($record['contextid']); unset($record['contextid']); if (!in_array($context->contextlevel, array(CONTEXT_SYSTEM, CONTEXT_COURSE, CONTEXT_USER))) { throw new coding_exception('Wrong contextid passed in testing_repository_generator::create_instance() $record'); } $id = repository::static_function($this->get_typename(), 'create', $this->get_typename(), 0, $context, $record); } // If the theme was initialised while creating the repository instance, something somewhere called an output // function. Rather than leaving this as a hard-to-debug situation, let's make it fail with a clear error. $outputstartedafter = $PAGE->get_where_theme_was_initialised(); if ($outputstartedbefore === null && $outputstartedafter !== null) { throw new coding_exception('Creating a repository_' . $this->get_typename() . ' initialised the theme and output!', 'This should not happen. Creating a repository should be a pure back-end operation. Unnecessarily initialising ' . 'the output mechanism at the wrong time can cause subtle bugs and is a significant performance hit. There is ' . 'likely a call to an output function that caused it:' . PHP_EOL . PHP_EOL . format_backtrace($outputstartedafter, true)); } return $DB->get_record('repository_instances', array('id' => $id), '*', MUST_EXIST); } /** * Create the type of repository. * * @param stdClass|array $record data to use to set up the type * @param array $options options for the set up of the type * * @return stdClass repository type record */ public function create_type($record = null, ?array $options = null) { global $CFG, $DB; require_once($CFG->dirroot . '/repository/lib.php'); $record = (array) $record; $type = $this->get_typename(); $typeoptions = repository::static_function($type, 'get_type_option_names'); $instanceoptions = repository::static_function($type, 'get_instance_option_names'); // The type allow for user and course instances. if (!empty($instanceoptions)) { $typeoptions[] = 'enableuserinstances'; $typeoptions[] = 'enablecourseinstances'; } // Make sure all the parameters are set. $record = $this->prepare_type_record($record); foreach ($typeoptions as $option) { if (!isset($record[$option])) { throw new coding_exception("$option must be present in testing::create_repository_type() for $type"); } } // Limit to allowed options. $record = array_intersect_key($record, array_flip($typeoptions)); // Create the type. $plugintype = new repository_type($type, $record); $plugintype->create(false); return $DB->get_record('repository', array('type' => $type)); } } PKx\韕lib.phpnu[PK5\=.33#=behat_core_competency_generator.phpnu[PKN\EA  (9behat_core_payment_generator.phpnu[PKz\ c Bbehat_mod_quiz_generator.phpnu[PK\Lbehat_mod_lti_generator.phpnu[PKY\)mHH[behat_core_ai_generator.phpnu[PK\~ZZ5ccomponent_generator_base.phpnu[PK\E zdata_generator.phpnu[PK\ =fdefault_block_generator.phpnu[PK\/b7mblock_generator.phpnu[PK\ψ 3434module_generator.phpnu[PK\̇repository_generator.phpnu[PK $