MDL-70412 groups: Improving create_group data generator
[moodle.git] / lib / testing / generator / data_generator.php
CommitLineData
7e7cfe7a
PS
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Data generator.
19 *
20 * @package core
6b219869 21 * @category test
7e7cfe7a
PS
22 * @copyright 2012 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
c29e3e24 26defined('MOODLE_INTERNAL') || die();
7e7cfe7a
PS
27
28/**
6b219869 29 * Data generator class for unit tests and other tools that need to create fake test sites.
7e7cfe7a
PS
30 *
31 * @package core
6b219869 32 * @category test
7e7cfe7a
PS
33 * @copyright 2012 Petr Skoda {@link http://skodak.org}
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
5c3c2c81 36class testing_data_generator {
cfa91962 37 /** @var int The number of grade categories created */
15ace204 38 protected $gradecategorycounter = 0;
cfa91962
JO
39 /** @var int The number of grade items created */
40 protected $gradeitemcounter = 0;
41 /** @var int The number of grade outcomes created */
42 protected $gradeoutcomecounter = 0;
7e7cfe7a
PS
43 protected $usercounter = 0;
44 protected $categorycount = 0;
4729332b 45 protected $cohortcount = 0;
7e7cfe7a
PS
46 protected $coursecount = 0;
47 protected $scalecount = 0;
48 protected $groupcount = 0;
49 protected $groupingcount = 0;
702851c0 50 protected $rolecount = 0;
e0c86198 51 protected $tagcount = 0;
7e7cfe7a
PS
52
53 /** @var array list of plugin generators */
54 protected $generators = array();
55
56 /** @var array lis of common last names */
57 public $lastnames = array(
58 'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'García', 'Rodríguez', 'Wilson',
59 'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Meyer', 'Weber', 'Schulz', 'Wagner', 'Becker', 'Hoffmann',
60 'Novák', 'Svoboda', 'Novotný', 'Dvořák', 'Černý', 'Procházková', 'Kučerová', 'Veselá', 'Horáková', 'Němcová',
61 'Смирнов', 'Иванов', 'Кузнецов', 'Соколов', 'Попов', 'Лебедева', 'Козлова', 'Новикова', 'Морозова', 'Петрова',
62 '王', '李', '张', '刘', '陈', '楊', '黃', '趙', '吳', '周',
63 '佐藤', '鈴木', '高橋', '田中', '渡辺', '伊藤', '山本', '中村', '小林', '斎藤',
64 );
65
66 /** @var array lis of common first names */
67 public $firstnames = array(
68 'Jacob', 'Ethan', 'Michael', 'Jayden', 'William', 'Isabella', 'Sophia', 'Emma', 'Olivia', 'Ava',
69 'Lukas', 'Leon', 'Luca', 'Timm', 'Paul', 'Leonie', 'Leah', 'Lena', 'Hanna', 'Laura',
70 'Jakub', 'Jan', 'Tomáš', 'Lukáš', 'Matěj', 'Tereza', 'Eliška', 'Anna', 'Adéla', 'Karolína',
71 'Даниил', 'Максим', 'Артем', 'Иван', 'Александр', 'София', 'Анастасия', 'Дарья', 'Мария', 'Полина',
72 '伟', '伟', '芳', '伟', '秀英', '秀英', '娜', '秀英', '伟', '敏',
73 '翔', '大翔', '拓海', '翔太', '颯太', '陽菜', 'さくら', '美咲', '葵', '美羽',
74 );
75
76 public $loremipsum = <<<EOD
77Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nulla non arcu lacinia neque faucibus fringilla. Vivamus porttitor turpis ac leo. Integer in sapien. Nullam eget nisl. Aliquam erat volutpat. Cras elementum. Mauris suscipit, ligula sit amet pharetra semper, nibh ante cursus purus, vel sagittis velit mauris vel metus. Integer malesuada. Nullam lectus justo, vulputate eget mollis sed, tempor sed magna. Mauris elementum mauris vitae tortor. Aliquam erat volutpat.
78Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Pellentesque ipsum. Cras pede libero, dapibus nec, pretium sit amet, tempor quis. Aliquam ante. Proin in tellus sit amet nibh dignissim sagittis. Vivamus porttitor turpis ac leo. Duis bibendum, lectus ut viverra rhoncus, dolor nunc faucibus libero, eget facilisis enim ipsum id lacus. In sem justo, commodo ut, suscipit at, pharetra vitae, orci. Aliquam erat volutpat. Nulla est.
79Vivamus luctus egestas leo. Aenean fermentum risus id tortor. Mauris dictum facilisis augue. Aliquam erat volutpat. Aliquam ornare wisi eu metus. Aliquam id dolor. Duis condimentum augue id magna semper rutrum. Donec iaculis gravida nulla. Pellentesque ipsum. Etiam dictum tincidunt diam. Quisque tincidunt scelerisque libero. Etiam egestas wisi a erat.
80Integer lacinia. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Mauris tincidunt sem sed arcu. Nullam feugiat, turpis at pulvinar vulputate, erat libero tristique tellus, nec bibendum odio risus sit amet ante. Aliquam id dolor. Maecenas sollicitudin. Et harum quidem rerum facilis est et expedita distinctio. Mauris suscipit, ligula sit amet pharetra semper, nibh ante cursus purus, vel sagittis velit mauris vel metus. Nullam dapibus fermentum ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Pellentesque sapien. Duis risus. Mauris elementum mauris vitae tortor. Suspendisse nisl. Integer rutrum, orci vestibulum ullamcorper ultricies, lacus quam ultricies odio, vitae placerat pede sem sit amet enim.
81In laoreet, magna id viverra tincidunt, sem odio bibendum justo, vel imperdiet sapien wisi sed libero. Proin pede metus, vulputate nec, fermentum fringilla, vehicula vitae, justo. Nullam justo enim, consectetuer nec, ullamcorper ac, vestibulum in, elit. Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? Maecenas lorem. Etiam posuere lacus quis dolor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Curabitur ligula sapien, pulvinar a vestibulum quis, facilisis vel sapien. Nam sed tellus id magna elementum tincidunt. Suspendisse nisl. Vivamus luctus egestas leo. Nulla non arcu lacinia neque faucibus fringilla. Etiam dui sem, fermentum vitae, sagittis id, malesuada in, quam. Etiam dictum tincidunt diam. Etiam commodo dui eget wisi. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Proin pede metus, vulputate nec, fermentum fringilla, vehicula vitae, justo. Duis ante orci, molestie vitae vehicula venenatis, tincidunt ac pede. Pellentesque sapien.
82EOD;
83
84 /**
85 * To be called from data reset code only,
86 * do not use in tests.
87 * @return void
88 */
89 public function reset() {
90 $this->usercounter = 0;
91 $this->categorycount = 0;
92 $this->coursecount = 0;
93 $this->scalecount = 0;
94
5c3c2c81 95 foreach ($this->generators as $generator) {
7e7cfe7a
PS
96 $generator->reset();
97 }
98 }
99
100 /**
ba203de1
TH
101 * Return generator for given plugin or component.
102 * @param string $component the component name, e.g. 'mod_forum' or 'core_question'.
103 * @return component_generator_base or rather an instance of the appropriate subclass.
7e7cfe7a
PS
104 */
105 public function get_plugin_generator($component) {
56da374e 106 list($type, $plugin) = core_component::normalize_component($component);
ba203de1
TH
107 $cleancomponent = $type . '_' . $plugin;
108 if ($cleancomponent != $component) {
109 debugging("Please specify the component you want a generator for as " .
110 "{$cleancomponent}, not {$component}.", DEBUG_DEVELOPER);
111 $component = $cleancomponent;
112 }
7e7cfe7a 113
ba203de1
TH
114 if (isset($this->generators[$component])) {
115 return $this->generators[$component];
7e7cfe7a
PS
116 }
117
b0d1d941 118 $dir = core_component::get_component_directory($component);
ba203de1
TH
119 $lib = $dir . '/tests/generator/lib.php';
120 if (!$dir || !is_readable($lib)) {
121 throw new coding_exception("Component {$component} does not support " .
122 "generators yet. Missing tests/generator/lib.php.");
123 }
7e7cfe7a 124
ba203de1
TH
125 include_once($lib);
126 $classname = $component . '_generator';
127
128 if (!class_exists($classname)) {
129 throw new coding_exception("Component {$component} does not support " .
130 "data generators yet. Class {$classname} not found.");
7e7cfe7a
PS
131 }
132
ba203de1
TH
133 $this->generators[$component] = new $classname($this);
134 return $this->generators[$component];
7e7cfe7a
PS
135 }
136
137 /**
138 * Create a test user
139 * @param array|stdClass $record
140 * @param array $options
141 * @return stdClass user record
142 */
143 public function create_user($record=null, array $options=null) {
144 global $DB, $CFG;
5b529aca 145 require_once($CFG->dirroot.'/user/lib.php');
7e7cfe7a
PS
146
147 $this->usercounter++;
148 $i = $this->usercounter;
149
150 $record = (array)$record;
151
152 if (!isset($record['auth'])) {
153 $record['auth'] = 'manual';
154 }
155
156 if (!isset($record['firstname']) and !isset($record['lastname'])) {
157 $country = rand(0, 5);
158 $firstname = rand(0, 4);
159 $lastname = rand(0, 4);
160 $female = rand(0, 1);
161 $record['firstname'] = $this->firstnames[($country*10) + $firstname + ($female*5)];
162 $record['lastname'] = $this->lastnames[($country*10) + $lastname + ($female*5)];
163
164 } else if (!isset($record['firstname'])) {
165 $record['firstname'] = 'Firstname'.$i;
166
167 } else if (!isset($record['lastname'])) {
168 $record['lastname'] = 'Lastname'.$i;
169 }
170
a327f25e
AG
171 if (!isset($record['firstnamephonetic'])) {
172 $firstnamephonetic = rand(0, 59);
173 $record['firstnamephonetic'] = $this->firstnames[$firstnamephonetic];
174 }
175
e5035ecb 176 if (!isset($record['lastnamephonetic'])) {
a327f25e
AG
177 $lastnamephonetic = rand(0, 59);
178 $record['lastnamephonetic'] = $this->lastnames[$lastnamephonetic];
179 }
180
181 if (!isset($record['middlename'])) {
182 $middlename = rand(0, 59);
183 $record['middlename'] = $this->firstnames[$middlename];
184 }
185
186 if (!isset($record['alternatename'])) {
187 $alternatename = rand(0, 59);
188 $record['alternatename'] = $this->firstnames[$alternatename];
189 }
190
7e7cfe7a
PS
191 if (!isset($record['idnumber'])) {
192 $record['idnumber'] = '';
193 }
194
195 if (!isset($record['mnethostid'])) {
196 $record['mnethostid'] = $CFG->mnet_localhost_id;
197 }
198
199 if (!isset($record['username'])) {
fe67134e
PS
200 $record['username'] = 'username'.$i;
201 $j = 2;
7e7cfe7a 202 while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
fe67134e
PS
203 $record['username'] = 'username'.$i.'_'.$j;
204 $j++;
7e7cfe7a
PS
205 }
206 }
207
dbf60a04
PS
208 if (isset($record['password'])) {
209 $record['password'] = hash_internal_user_password($record['password']);
7e7cfe7a
PS
210 }
211
212 if (!isset($record['email'])) {
213 $record['email'] = $record['username'].'@example.com';
214 }
215
216 if (!isset($record['confirmed'])) {
217 $record['confirmed'] = 1;
218 }
219
1fefa3ce
SL
220 if (!isset($record['lastip'])) {
221 $record['lastip'] = '0.0.0.0';
222 }
7e7cfe7a 223
5b529aca
MG
224 $tobedeleted = !empty($record['deleted']);
225 unset($record['deleted']);
7e7cfe7a 226
5b529aca 227 $userid = user_create_user($record, false, false);
7e7cfe7a 228
5b529aca
MG
229 if ($extrafields = array_intersect_key($record, ['password' => 1, 'timecreated' => 1])) {
230 $DB->update_record('user', ['id' => $userid] + $extrafields);
231 }
3204deac 232
5b529aca 233 if (!$tobedeleted) {
3204deac
AA
234 // All new not deleted users must have a favourite self-conversation.
235 $selfconversation = \core_message\api::create_conversation(
236 \core_message\api::MESSAGE_CONVERSATION_TYPE_SELF,
237 [$userid]
238 );
239 \core_message\api::set_favourite_conversation($selfconversation->id, $userid);
5b529aca
MG
240
241 // Save custom profile fields data.
242 $hasprofilefields = array_filter($record, function($key){
243 return strpos($key, 'profile_field_') === 0;
244 }, ARRAY_FILTER_USE_KEY);
245 if ($hasprofilefields) {
246 require_once($CFG->dirroot.'/user/profile/lib.php');
247 $usernew = (object)(['id' => $userid] + $record);
248 profile_save_data($usernew);
249 }
7e7cfe7a
PS
250 }
251
810805da
MG
252 $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
253
5b529aca 254 if (!$tobedeleted && isset($record['interests'])) {
810805da
MG
255 require_once($CFG->dirroot . '/user/editlib.php');
256 if (!is_array($record['interests'])) {
257 $record['interests'] = preg_split('/\s*,\s*/', trim($record['interests']), -1, PREG_SPLIT_NO_EMPTY);
258 }
259 useredit_update_interests($user, $record['interests']);
260 }
261
5b529aca
MG
262 \core\event\user_created::create_from_userid($userid)->trigger();
263
264 if ($tobedeleted) {
265 delete_user($user);
266 $user = $DB->get_record('user', array('id' => $userid));
267 }
810805da 268 return $user;
7e7cfe7a
PS
269 }
270
271 /**
272 * Create a test course category
273 * @param array|stdClass $record
274 * @param array $options
442f12f8 275 * @return core_course_category course category record
7e7cfe7a 276 */
4729332b 277 public function create_category($record=null, array $options=null) {
7e7cfe7a
PS
278 $this->categorycount++;
279 $i = $this->categorycount;
280
281 $record = (array)$record;
282
283 if (!isset($record['name'])) {
284 $record['name'] = 'Course category '.$i;
285 }
286
7e7cfe7a
PS
287 if (!isset($record['description'])) {
288 $record['description'] = "Test course category $i\n$this->loremipsum";
289 }
290
b28bb7e8
MG
291 if (!isset($record['idnumber'])) {
292 $record['idnumber'] = '';
7e7cfe7a 293 }
7e7cfe7a 294
442f12f8 295 return core_course_category::create($record);
7e7cfe7a
PS
296 }
297
4729332b
PS
298 /**
299 * Create test cohort.
300 * @param array|stdClass $record
301 * @param array $options
302 * @return stdClass cohort record
303 */
304 public function create_cohort($record=null, array $options=null) {
305 global $DB, $CFG;
306 require_once("$CFG->dirroot/cohort/lib.php");
307
308 $this->cohortcount++;
309 $i = $this->cohortcount;
310
311 $record = (array)$record;
312
313 if (!isset($record['contextid'])) {
314 $record['contextid'] = context_system::instance()->id;
315 }
316
317 if (!isset($record['name'])) {
318 $record['name'] = 'Cohort '.$i;
319 }
320
321 if (!isset($record['idnumber'])) {
322 $record['idnumber'] = '';
323 }
324
325 if (!isset($record['description'])) {
aac98ca7 326 $record['description'] = "Description for '{$record['name']}' \n$this->loremipsum";
4729332b
PS
327 }
328
329 if (!isset($record['descriptionformat'])) {
330 $record['descriptionformat'] = FORMAT_MOODLE;
331 }
332
80f98467
MG
333 if (!isset($record['visible'])) {
334 $record['visible'] = 1;
335 }
336
4729332b
PS
337 if (!isset($record['component'])) {
338 $record['component'] = '';
339 }
340
341 $id = cohort_add_cohort((object)$record);
342
343 return $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST);
344 }
345
7e7cfe7a
PS
346 /**
347 * Create a test course
348 * @param array|stdClass $record
349 * @param array $options with keys:
350 * 'createsections'=>bool precreate all sections
351 * @return stdClass course record
352 */
4729332b 353 public function create_course($record=null, array $options=null) {
7e7cfe7a
PS
354 global $DB, $CFG;
355 require_once("$CFG->dirroot/course/lib.php");
356
357 $this->coursecount++;
358 $i = $this->coursecount;
359
360 $record = (array)$record;
361
362 if (!isset($record['fullname'])) {
363 $record['fullname'] = 'Test course '.$i;
364 }
365
366 if (!isset($record['shortname'])) {
367 $record['shortname'] = 'tc_'.$i;
368 }
369
370 if (!isset($record['idnumber'])) {
371 $record['idnumber'] = '';
372 }
373
374 if (!isset($record['format'])) {
375 $record['format'] = 'topics';
376 }
377
378 if (!isset($record['newsitems'])) {
379 $record['newsitems'] = 0;
380 }
381
382 if (!isset($record['numsections'])) {
383 $record['numsections'] = 5;
384 }
385
4a38e659
PS
386 if (!isset($record['summary'])) {
387 $record['summary'] = "Test course $i\n$this->loremipsum";
7e7cfe7a
PS
388 }
389
4a38e659
PS
390 if (!isset($record['summaryformat'])) {
391 $record['summaryformat'] = FORMAT_MOODLE;
7e7cfe7a
PS
392 }
393
394 if (!isset($record['category'])) {
395 $record['category'] = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
396 }
397
68c2b726
AN
398 if (!isset($record['startdate'])) {
399 $record['startdate'] = usergetmidnight(time());
400 }
401
810805da
MG
402 if (isset($record['tags']) && !is_array($record['tags'])) {
403 $record['tags'] = preg_split('/\s*,\s*/', trim($record['tags']), -1, PREG_SPLIT_NO_EMPTY);
404 }
405
89b909f6
MG
406 if (!empty($options['createsections']) && empty($record['numsections'])) {
407 // Since Moodle 3.3 function create_course() automatically creates sections if numsections is specified.
408 // For BC if 'createsections' is given but 'numsections' is not, assume the default value from config.
409 $record['numsections'] = get_config('moodlecourse', 'numsections');
410 }
411
01c10959 412 if (!empty($record['customfields'])) {
bbf60b14 413 foreach ($record['customfields'] as $field) {
01c10959
DNA
414 $record['customfield_'.$field['shortname']] = $field['value'];
415 }
416 }
417
7e7cfe7a
PS
418 $course = create_course((object)$record);
419 context_course::instance($course->id);
7e7cfe7a
PS
420
421 return $course;
422 }
423
424 /**
425 * Create course section if does not exist yet
384c3510 426 * @param array|stdClass $record must contain 'course' and 'section' attributes
7e7cfe7a
PS
427 * @param array|null $options
428 * @return stdClass
429 * @throws coding_exception
430 */
431 public function create_course_section($record = null, array $options = null) {
432 global $DB;
433
434 $record = (array)$record;
435
436 if (empty($record['course'])) {
5c3c2c81 437 throw new coding_exception('course must be present in testing_data_generator::create_course_section() $record');
7e7cfe7a
PS
438 }
439
440 if (!isset($record['section'])) {
5c3c2c81 441 throw new coding_exception('section must be present in testing_data_generator::create_course_section() $record');
7e7cfe7a
PS
442 }
443
b46be6ad
MG
444 course_create_sections_if_missing($record['course'], $record['section']);
445 return get_fast_modinfo($record['course'])->get_section_info($record['section']);
7e7cfe7a
PS
446 }
447
448 /**
eb3884e4
TH
449 * Create a test block.
450 *
451 * The $record passed in becomes the basis for the new row added to the
452 * block_instances table. You only need to supply the values of interest.
453 * Any missing values have sensible defaults filled in, and ->blockname will be set based on $blockname.
454 *
455 * The $options array provides additional data, not directly related to what
456 * will be inserted in the block_instance table, which may affect the block
457 * that is created. The meanings of any data passed here depends on the particular
458 * type of block being created.
459 *
460 * @param string $blockname the type of block to create. E.g. 'html'.
461 * @param array|stdClass $record forms the basis for the entry to be inserted in the block_instances table.
462 * @param array $options further, block-specific options to control how the block is created.
463 * @return stdClass new block_instance record.
7e7cfe7a 464 */
551088fc 465 public function create_block($blockname, $record=null, array $options=array()) {
7e7cfe7a
PS
466 $generator = $this->get_plugin_generator('block_'.$blockname);
467 return $generator->create_instance($record, $options);
468 }
469
470 /**
eb3884e4
TH
471 * Create a test activity module.
472 *
473 * The $record should contain the same data that you would call from
474 * ->get_data() when the mod_[type]_mod_form is submitted, except that you
475 * only need to supply values of interest. The only required value is
476 * 'course'. Any missing values will have a sensible default supplied.
477 *
478 * The $options array provides additional data, not directly related to what
479 * would come back from the module edit settings form, which may affect the activity
480 * that is created. The meanings of any data passed here depends on the particular
481 * type of activity being created.
482 *
483 * @param string $modulename the type of activity to create. E.g. 'forum' or 'quiz'.
484 * @param array|stdClass $record data, as if from the module edit settings form.
485 * @param array $options additional data that may affect how the module is created.
486 * @return stdClass activity record new new record that was just inserted in the table
487 * like 'forum' or 'quiz', with a ->cmid field added.
7e7cfe7a
PS
488 */
489 public function create_module($modulename, $record=null, array $options=null) {
490 $generator = $this->get_plugin_generator('mod_'.$modulename);
491 return $generator->create_instance($record, $options);
492 }
493
494 /**
495 * Create a test group for the specified course
496 *
497 * $record should be either an array or a stdClass containing infomation about the group to create.
498 * At the very least it needs to contain courseid.
499 * Default values are added for name, description, and descriptionformat if they are not present.
500 *
6b219869
DM
501 * This function calls groups_create_group() to create the group within the database.
502 * @see groups_create_group
7e7cfe7a
PS
503 * @param array|stdClass $record
504 * @return stdClass group record
505 */
506 public function create_group($record) {
507 global $DB, $CFG;
508
509 require_once($CFG->dirroot . '/group/lib.php');
510
511 $this->groupcount++;
9798fb06 512 $i = str_pad($this->groupcount, 4, '0', STR_PAD_LEFT);
7e7cfe7a
PS
513
514 $record = (array)$record;
515
516 if (empty($record['courseid'])) {
5c3c2c81 517 throw new coding_exception('courseid must be present in testing_data_generator::create_group() $record');
7e7cfe7a
PS
518 }
519
520 if (!isset($record['name'])) {
521 $record['name'] = 'group-' . $i;
522 }
523
524 if (!isset($record['description'])) {
525 $record['description'] = "Test Group $i\n{$this->loremipsum}";
526 }
527
528 if (!isset($record['descriptionformat'])) {
529 $record['descriptionformat'] = FORMAT_MOODLE;
530 }
531
532 $id = groups_create_group((object)$record);
533
19237383
JD
534 // Allow tests to set group pictures.
535 if (!empty($record['picturepath'])) {
536 require_once($CFG->dirroot . '/lib/gdlib.php');
537 $grouppicture = process_new_icon(\context_course::instance($record['courseid']), 'group', 'icon', $id,
538 $record['picturepath']);
539
540 $DB->set_field('groups', 'picture', $grouppicture, ['id' => $id]);
541
542 // Invalidate the group data as we've updated the group record.
543 cache_helper::invalidate_by_definition('core', 'groupdata', array(), [$record['courseid']]);
544 }
545
7e7cfe7a
PS
546 return $DB->get_record('groups', array('id'=>$id));
547 }
548
87bb583c
DM
549 /**
550 * Create a test group member
551 * @param array|stdClass $record
552 * @throws coding_exception
553 * @return boolean
554 */
555 public function create_group_member($record) {
556 global $DB, $CFG;
557
558 require_once($CFG->dirroot . '/group/lib.php');
559
560 $record = (array)$record;
561
562 if (empty($record['userid'])) {
563 throw new coding_exception('user must be present in testing_util::create_group_member() $record');
564 }
565
566 if (!isset($record['groupid'])) {
567 throw new coding_exception('group must be present in testing_util::create_group_member() $record');
568 }
569
570 if (!isset($record['component'])) {
571 $record['component'] = null;
572 }
573 if (!isset($record['itemid'])) {
574 $record['itemid'] = 0;
575 }
576
577 return groups_add_member($record['groupid'], $record['userid'], $record['component'], $record['itemid']);
578 }
579
7e7cfe7a
PS
580 /**
581 * Create a test grouping for the specified course
582 *
583 * $record should be either an array or a stdClass containing infomation about the grouping to create.
584 * At the very least it needs to contain courseid.
585 * Default values are added for name, description, and descriptionformat if they are not present.
586 *
6b219869
DM
587 * This function calls groups_create_grouping() to create the grouping within the database.
588 * @see groups_create_grouping
7e7cfe7a
PS
589 * @param array|stdClass $record
590 * @return stdClass grouping record
591 */
592 public function create_grouping($record) {
593 global $DB, $CFG;
594
595 require_once($CFG->dirroot . '/group/lib.php');
596
597 $this->groupingcount++;
598 $i = $this->groupingcount;
599
600 $record = (array)$record;
601
602 if (empty($record['courseid'])) {
5c3c2c81 603 throw new coding_exception('courseid must be present in testing_data_generator::create_grouping() $record');
7e7cfe7a
PS
604 }
605
606 if (!isset($record['name'])) {
607 $record['name'] = 'grouping-' . $i;
608 }
609
610 if (!isset($record['description'])) {
611 $record['description'] = "Test Grouping $i\n{$this->loremipsum}";
612 }
613
614 if (!isset($record['descriptionformat'])) {
615 $record['descriptionformat'] = FORMAT_MOODLE;
616 }
617
618 $id = groups_create_grouping((object)$record);
619
620 return $DB->get_record('groupings', array('id'=>$id));
621 }
622
87bb583c
DM
623 /**
624 * Create a test grouping group
625 * @param array|stdClass $record
626 * @throws coding_exception
627 * @return boolean
628 */
629 public function create_grouping_group($record) {
630 global $DB, $CFG;
631
632 require_once($CFG->dirroot . '/group/lib.php');
633
634 $record = (array)$record;
635
636 if (empty($record['groupingid'])) {
637 throw new coding_exception('grouping must be present in testing::create_grouping_group() $record');
638 }
639
640 if (!isset($record['groupid'])) {
641 throw new coding_exception('group must be present in testing_util::create_grouping_group() $record');
642 }
643
644 return groups_assign_grouping($record['groupingid'], $record['groupid']);
645 }
646
0852bbae
FM
647 /**
648 * Create an instance of a repository.
649 *
650 * @param string type of repository to create an instance for.
651 * @param array|stdClass $record data to use to up set the instance.
652 * @param array $options options
653 * @return stdClass repository instance record
5bcfd504 654 * @since Moodle 2.5.1
0852bbae
FM
655 */
656 public function create_repository($type, $record=null, array $options = null) {
0852bbae
FM
657 $generator = $this->get_plugin_generator('repository_'.$type);
658 return $generator->create_instance($record, $options);
659 }
660
661 /**
662 * Create an instance of a repository.
663 *
664 * @param string type of repository to create an instance for.
665 * @param array|stdClass $record data to use to up set the instance.
666 * @param array $options options
667 * @return repository_type object
5bcfd504 668 * @since Moodle 2.5.1
0852bbae
FM
669 */
670 public function create_repository_type($type, $record=null, array $options = null) {
0852bbae
FM
671 $generator = $this->get_plugin_generator('repository_'.$type);
672 return $generator->create_type($record, $options);
673 }
674
675
7e7cfe7a
PS
676 /**
677 * Create a test scale
678 * @param array|stdClass $record
679 * @param array $options
680 * @return stdClass block instance record
681 */
682 public function create_scale($record=null, array $options=null) {
683 global $DB;
684
685 $this->scalecount++;
686 $i = $this->scalecount;
687
688 $record = (array)$record;
689
690 if (!isset($record['name'])) {
691 $record['name'] = 'Test scale '.$i;
692 }
693
694 if (!isset($record['scale'])) {
695 $record['scale'] = 'A,B,C,D,F';
696 }
697
698 if (!isset($record['courseid'])) {
699 $record['courseid'] = 0;
700 }
701
702 if (!isset($record['userid'])) {
703 $record['userid'] = 0;
704 }
705
706 if (!isset($record['description'])) {
707 $record['description'] = 'Test scale description '.$i;
708 }
709
710 if (!isset($record['descriptionformat'])) {
711 $record['descriptionformat'] = FORMAT_MOODLE;
712 }
713
714 $record['timemodified'] = time();
715
716 if (isset($record['id'])) {
717 $DB->import_record('scale', $record);
718 $DB->get_manager()->reset_sequence('scale');
719 $id = $record['id'];
720 } else {
721 $id = $DB->insert_record('scale', $record);
722 }
723
724 return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST);
725 }
4f5789ea 726
702851c0
DM
727 /**
728 * Creates a new role in the system.
729 *
730 * You can fill $record with the role 'name',
731 * 'shortname', 'description' and 'archetype'.
732 *
733 * If an archetype is specified it's capabilities,
734 * context where the role can be assigned and
735 * all other properties are copied from the archetype;
736 * if no archetype is specified it will create an
737 * empty role.
738 *
739 * @param array|stdClass $record
740 * @return int The new role id
741 */
742 public function create_role($record=null) {
743 global $DB;
744
745 $this->rolecount++;
746 $i = $this->rolecount;
747
748 $record = (array)$record;
749
750 if (empty($record['shortname'])) {
751 $record['shortname'] = 'role-' . $i;
752 }
753
754 if (empty($record['name'])) {
755 $record['name'] = 'Test role ' . $i;
756 }
757
758 if (empty($record['description'])) {
759 $record['description'] = 'Test role ' . $i . ' description';
760 }
761
762 if (empty($record['archetype'])) {
763 $record['archetype'] = '';
764 } else {
765 $archetypes = get_role_archetypes();
766 if (empty($archetypes[$record['archetype']])) {
767 throw new coding_exception('\'role\' requires the field \'archetype\' to specify a ' .
768 'valid archetype shortname (editingteacher, student...)');
769 }
770 }
771
772 // Creates the role.
773 if (!$newroleid = create_role($record['name'], $record['shortname'], $record['description'], $record['archetype'])) {
774 throw new coding_exception('There was an error creating \'' . $record['shortname'] . '\' role');
775 }
776
777 // If no archetype was specified we allow it to be added to all contexts,
778 // otherwise we allow it in the archetype contexts.
779 if (!$record['archetype']) {
780 $contextlevels = array_keys(context_helper::get_all_levels());
781 } else {
782 // Copying from the archetype default rol.
783 $archetyperoleid = $DB->get_field(
784 'role',
785 'id',
786 array('shortname' => $record['archetype'], 'archetype' => $record['archetype'])
787 );
788 $contextlevels = get_role_contextlevels($archetyperoleid);
789 }
790 set_role_contextlevels($newroleid, $contextlevels);
791
792 if ($record['archetype']) {
793
a63cd3e2 794 // We copy all the roles the archetype can assign, override, switch to and view.
702851c0 795 if ($record['archetype']) {
a63cd3e2 796 $types = array('assign', 'override', 'switch', 'view');
702851c0
DM
797 foreach ($types as $type) {
798 $rolestocopy = get_default_role_archetype_allows($type, $record['archetype']);
799 foreach ($rolestocopy as $tocopy) {
64cd4596 800 $functionname = "core_role_set_{$type}_allowed";
702851c0
DM
801 $functionname($newroleid, $tocopy);
802 }
803 }
804 }
805
806 // Copying the archetype capabilities.
807 $sourcerole = $DB->get_record('role', array('id' => $archetyperoleid));
808 role_cap_duplicate($sourcerole, $newroleid);
809 }
810
811 return $newroleid;
812 }
813
e0c86198
MN
814 /**
815 * Create a tag.
816 *
817 * @param array|stdClass $record
818 * @return stdClass the tag record
819 */
820 public function create_tag($record = null) {
821 global $DB, $USER;
822
823 $this->tagcount++;
824 $i = $this->tagcount;
825
826 $record = (array) $record;
827
828 if (!isset($record['userid'])) {
829 $record['userid'] = $USER->id;
830 }
831
9a7e3986
MG
832 if (!isset($record['rawname'])) {
833 if (isset($record['name'])) {
834 $record['rawname'] = $record['name'];
835 } else {
836 $record['rawname'] = 'Tag name ' . $i;
837 }
e0c86198
MN
838 }
839
9a7e3986
MG
840 // Attribute 'name' should be a lowercase version of 'rawname', if not set.
841 if (!isset($record['name'])) {
842 $record['name'] = core_text::strtolower($record['rawname']);
843 } else {
844 $record['name'] = core_text::strtolower($record['name']);
e0c86198
MN
845 }
846
c026a28d
MG
847 if (!isset($record['tagcollid'])) {
848 $record['tagcollid'] = core_tag_collection::get_default();
849 }
850
e0c86198
MN
851 if (!isset($record['description'])) {
852 $record['description'] = 'Tag description';
853 }
854
855 if (!isset($record['descriptionformat'])) {
856 $record['descriptionformat'] = FORMAT_MOODLE;
857 }
858
859 if (!isset($record['flag'])) {
860 $record['flag'] = 0;
861 }
862
863 if (!isset($record['timemodified'])) {
864 $record['timemodified'] = time();
865 }
866
867 $id = $DB->insert_record('tag', $record);
868
869 return $DB->get_record('tag', array('id' => $id), '*', MUST_EXIST);
870 }
871
ba203de1
TH
872 /**
873 * Helper method which combines $defaults with the values specified in $record.
874 * If $record is an object, it is converted to an array.
875 * Then, for each key that is in $defaults, but not in $record, the value
876 * from $defaults is copied.
877 * @param array $defaults the default value for each field with
878 * @param array|stdClass $record
879 * @return array updated $record.
880 */
881 public function combine_defaults_and_record(array $defaults, $record) {
882 $record = (array) $record;
883
884 foreach ($defaults as $key => $defaults) {
885 if (!array_key_exists($key, $record)) {
886 $record[$key] = $defaults;
887 }
888 }
889 return $record;
890 }
891
4f5789ea
PS
892 /**
893 * Simplified enrolment of user to course using default options.
894 *
895 * It is strongly recommended to use only this method for 'manual' and 'self' plugins only!!!
896 *
897 * @param int $userid
898 * @param int $courseid
92cce140 899 * @param int|string $roleidorshortname optional role id or role shortname, use only with manual plugin
4f5789ea
PS
900 * @param string $enrol name of enrol plugin,
901 * there must be exactly one instance in course,
902 * it must support enrol_user() method.
1ecb8044
RT
903 * @param int $timestart (optional) 0 means unknown
904 * @param int $timeend (optional) 0 means forever
905 * @param int $status (optional) default to ENROL_USER_ACTIVE for new enrolments
4f5789ea
PS
906 * @return bool success
907 */
92cce140 908 public function enrol_user($userid, $courseid, $roleidorshortname = null, $enrol = 'manual',
909 $timestart = 0, $timeend = 0, $status = null) {
4f5789ea
PS
910 global $DB;
911
92cce140 912 // If role is specified by shortname, convert it into an id.
913 if (!is_numeric($roleidorshortname) && is_string($roleidorshortname)) {
914 $roleid = $DB->get_field('role', 'id', array('shortname' => $roleidorshortname), MUST_EXIST);
915 } else {
916 $roleid = $roleidorshortname;
917 }
918
4f5789ea
PS
919 if (!$plugin = enrol_get_plugin($enrol)) {
920 return false;
921 }
922
923 $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'enrol'=>$enrol));
924 if (count($instances) != 1) {
925 return false;
926 }
927 $instance = reset($instances);
928
929 if (is_null($roleid) and $instance->roleid) {
930 $roleid = $instance->roleid;
931 }
932
e6cc5347 933 $plugin->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status);
4f5789ea
PS
934 return true;
935 }
72ddc05f
DM
936
937 /**
938 * Assigns the specified role to a user in the context.
939 *
940 * @param int $roleid
941 * @param int $userid
942 * @param int $contextid Defaults to the system context
943 * @return int new/existing id of the assignment
944 */
945 public function role_assign($roleid, $userid, $contextid = false) {
946
947 // Default to the system context.
948 if (!$contextid) {
949 $context = context_system::instance();
950 $contextid = $context->id;
951 }
952
953 if (empty($roleid)) {
954 throw new coding_exception('roleid must be present in testing_data_generator::role_assign() arguments');
955 }
956
957 if (empty($userid)) {
958 throw new coding_exception('userid must be present in testing_data_generator::role_assign() arguments');
959 }
960
961 return role_assign($roleid, $userid, $contextid);
962 }
963
15ace204
DW
964 /**
965 * Create a grade_category.
966 *
967 * @param array|stdClass $record
968 * @return stdClass the grade category record
969 */
970 public function create_grade_category($record = null) {
971 global $CFG;
972
973 $this->gradecategorycounter++;
15ace204 974
f4f2045a
MG
975 $record = (array)$record;
976
977 if (empty($record['courseid'])) {
978 throw new coding_exception('courseid must be present in testing::create_grade_category() $record');
979 }
980
15ace204 981 if (!isset($record['fullname'])) {
cfa91962 982 $record['fullname'] = 'Grade category ' . $this->gradecategorycounter;
15ace204
DW
983 }
984
985 // For gradelib classes.
986 require_once($CFG->libdir . '/gradelib.php');
987 // Create new grading category in this course.
f4f2045a 988 $gradecategory = new grade_category(array('courseid' => $record['courseid']), false);
15ace204 989 $gradecategory->apply_default_settings();
f4f2045a 990 grade_category::set_properties($gradecategory, $record);
15ace204
DW
991 $gradecategory->apply_forced_settings();
992 $gradecategory->insert();
f4f2045a 993
15ace204
DW
994 // This creates a default grade item for the category
995 $gradeitem = $gradecategory->load_grade_item();
996
15ace204
DW
997 $gradecategory->update_from_db();
998 return $gradecategory->get_record_data();
999 }
cfa91962
JO
1000
1001 /**
1002 * Create a grade_item.
1003 *
1004 * @param array|stdClass $record
1005 * @return stdClass the grade item record
1006 */
1007 public function create_grade_item($record = null) {
1008 global $CFG;
1009 require_once("$CFG->libdir/gradelib.php");
1010
1011 $this->gradeitemcounter++;
1012
1013 if (!isset($record['itemtype'])) {
1014 $record['itemtype'] = 'manual';
1015 }
1016
1017 if (!isset($record['itemname'])) {
1018 $record['itemname'] = 'Grade item ' . $this->gradeitemcounter;
1019 }
1020
1021 if (isset($record['outcomeid'])) {
1022 $outcome = new grade_outcome(array('id' => $record['outcomeid']));
1023 $record['scaleid'] = $outcome->scaleid;
1024 }
1025 if (isset($record['scaleid'])) {
1026 $record['gradetype'] = GRADE_TYPE_SCALE;
1027 } else if (!isset($record['gradetype'])) {
1028 $record['gradetype'] = GRADE_TYPE_VALUE;
1029 }
1030
1031 // Create new grade item in this course.
1032 $gradeitem = new grade_item($record, false);
1033 $gradeitem->insert();
1034
1035 $gradeitem->update_from_db();
1036 return $gradeitem->get_record_data();
1037 }
1038
1039 /**
1040 * Create a grade_outcome.
1041 *
1042 * @param array|stdClass $record
1043 * @return stdClass the grade outcome record
1044 */
1045 public function create_grade_outcome($record = null) {
1046 global $CFG;
1047
1048 $this->gradeoutcomecounter++;
1049 $i = $this->gradeoutcomecounter;
1050
1051 if (!isset($record['fullname'])) {
1052 $record['fullname'] = 'Grade outcome ' . $i;
1053 }
1054
1055 // For gradelib classes.
1056 require_once($CFG->libdir . '/gradelib.php');
1057 // Create new grading outcome in this course.
1058 $gradeoutcome = new grade_outcome($record, false);
1059 $gradeoutcome->insert();
1060
1061 $gradeoutcome->update_from_db();
1062 return $gradeoutcome->get_record_data();
1063 }
c314d6ed
JP
1064
1065 /**
1066 * Helper function used to create an LTI tool.
1067 *
1068 * @param array $data
1069 * @return stdClass the tool
1070 */
1071 public function create_lti_tool($data = array()) {
1072 global $DB;
1073
1074 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1075 $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
1076
1077 // Create a course if no course id was specified.
1078 if (empty($data->courseid)) {
1079 $course = $this->create_course();
1080 $data->courseid = $course->id;
1081 } else {
1082 $course = get_course($data->courseid);
1083 }
1084
1085 if (!empty($data->cmid)) {
1086 $data->contextid = context_module::instance($data->cmid)->id;
1087 } else {
1088 $data->contextid = context_course::instance($data->courseid)->id;
1089 }
1090
1091 // Set it to enabled if no status was specified.
1092 if (!isset($data->status)) {
1093 $data->status = ENROL_INSTANCE_ENABLED;
1094 }
1095
1096 // Add some extra necessary fields to the data.
1097 $data->name = 'Test LTI';
1098 $data->roleinstructor = $studentrole->id;
1099 $data->rolelearner = $teacherrole->id;
1100
1101 // Get the enrol LTI plugin.
1102 $enrolplugin = enrol_get_plugin('lti');
1103 $instanceid = $enrolplugin->add_instance($course, (array) $data);
1104
1105 // Get the tool associated with this instance.
1106 return $DB->get_record('enrol_lti_tools', array('enrolid' => $instanceid));
1107 }
fb3c0fc3
AN
1108
1109 /**
1110 * Helper function used to create an event.
1111 *
1112 * @param array $data
1113 * @return stdClass
1114 */
1115 public function create_event($data = []) {
1116 global $CFG;
1117
1118 require_once($CFG->dirroot . '/calendar/lib.php');
1119 $record = new \stdClass();
1120 $record->name = 'event name';
fb3c0fc3
AN
1121 $record->repeat = 0;
1122 $record->repeats = 0;
1123 $record->timestart = time();
1124 $record->timeduration = 0;
1125 $record->timesort = 0;
1126 $record->eventtype = 'user';
1127 $record->courseid = 0;
44ae0838 1128 $record->categoryid = 0;
fb3c0fc3
AN
1129
1130 foreach ($data as $key => $value) {
1131 $record->$key = $value;
1132 }
1133
1134 switch ($record->eventtype) {
1135 case 'user':
1136 unset($record->categoryid);
1137 unset($record->courseid);
1138 unset($record->groupid);
1139 break;
1140 case 'group':
1141 unset($record->categoryid);
1142 break;
1143 case 'course':
1144 unset($record->categoryid);
1145 unset($record->groupid);
1146 break;
1147 case 'category':
1148 unset($record->courseid);
1149 unset($record->groupid);
1150 break;
375d82ca 1151 case 'site':
fb3c0fc3
AN
1152 unset($record->categoryid);
1153 unset($record->courseid);
1154 unset($record->groupid);
1155 break;
1156 }
1157
1158 $event = new calendar_event($record);
1159 $event->create($record);
1160
1161 return $event->properties();
1162 }
e984917d 1163
01c10959
DNA
1164 /**
1165 * Create a new course custom field category with the given name.
1166 *
1167 * @param array $data Array with data['name'] of category
1168 * @return \core_customfield\category_controller The created category
1169 */
bbf60b14 1170 public function create_custom_field_category($data) : \core_customfield\category_controller {
01c10959
DNA
1171 return $this->get_plugin_generator('core_customfield')->create_category($data);
1172 }
1173
1174 /**
1175 * Create a new custom field
1176 *
1177 * @param array $data Array with 'name', 'shortname' and 'type' of the field
1178 * @return \core_customfield\field_controller The created field
1179 */
bbf60b14 1180 public function create_custom_field($data) : \core_customfield\field_controller {
01c10959
DNA
1181 global $DB;
1182 if (empty($data['categoryid']) && !empty($data['category'])) {
1183 $data['categoryid'] = $DB->get_field('customfield_category', 'id', ['name' => $data['category']]);
1184 unset($data['category']);
1185 }
1186 return $this->get_plugin_generator('core_customfield')->create_field($data);
1187 }
1188
9ddb51b0 1189 /**
1190 * Create a new category for custom profile fields.
1191 *
1192 * @param array $data Array with 'name' and optionally 'sortorder'
1193 * @return \stdClass New category object
1194 */
1195 public function create_custom_profile_field_category(array $data): \stdClass {
1196 global $DB;
1197
1198 // Pick next sortorder if not defined.
1199 if (!array_key_exists('sortorder', $data)) {
1200 $data['sortorder'] = (int)$DB->get_field_sql('SELECT MAX(sortorder) FROM {user_info_category}') + 1;
1201 }
1202
1203 $category = (object)[
1204 'name' => $data['name'],
1205 'sortorder' => $data['sortorder']
1206 ];
1207 $category->id = $DB->insert_record('user_info_category', $category);
1208
1209 return $category;
1210 }
1211
1212 /**
1213 * Creates a new custom profile field.
1214 *
1215 * Optional fields are:
1216 *
1217 * categoryid (or use 'category' to specify by name). If you don't specify
1218 * either, it will add the field to a 'Testing' category, which will be created for you if
1219 * necessary.
1220 *
1221 * sortorder (if you don't specify this, it will pick the next one in the category).
1222 *
1223 * all the other database fields (if you don't specify this, it will pick sensible defaults
1224 * based on the data type).
1225 *
1226 * @param array $data Array with 'datatype', 'shortname', and 'name'
1227 * @return \stdClass Database object from the user_info_field table
1228 */
1229 public function create_custom_profile_field(array $data): \stdClass {
1230 global $DB, $CFG;
1231 require_once($CFG->dirroot . '/user/profile/lib.php');
1232
1233 // Set up category if necessary.
1234 if (!array_key_exists('categoryid', $data)) {
1235 if (array_key_exists('category', $data)) {
1236 $data['categoryid'] = $DB->get_field('user_info_category', 'id',
1237 ['name' => $data['category']], MUST_EXIST);
1238 } else {
1239 // Make up a 'Testing' category or use existing.
1240 $data['categoryid'] = $DB->get_field('user_info_category', 'id', ['name' => 'Testing']);
1241 if (!$data['categoryid']) {
1242 $created = $this->create_custom_profile_field_category(['name' => 'Testing']);
1243 $data['categoryid'] = $created->id;
1244 }
1245 }
1246 }
1247
1248 // Pick sort order if necessary.
1249 if (!array_key_exists('sortorder', $data)) {
1250 $data['sortorder'] = (int)$DB->get_field_sql(
1251 'SELECT MAX(sortorder) FROM {user_info_field} WHERE categoryid = ?',
1252 [$data['categoryid']]) + 1;
1253 }
1254
1255 // Defaults for other values.
1256 $defaults = [
1257 'description' => '',
1258 'descriptionformat' => 0,
1259 'required' => 0,
1260 'locked' => 0,
1261 'visible' => PROFILE_VISIBLE_ALL,
1262 'forceunique' => 0,
1263 'signup' => 0,
1264 'defaultdata' => '',
1265 'defaultdataformat' => 0,
1266 'param1' => '',
1267 'param2' => '',
1268 'param3' => '',
1269 'param4' => '',
1270 'param5' => ''
1271 ];
1272
1273 // Type-specific defaults for other values.
1274 $typedefaults = [
1275 'text' => [
1276 'param1' => 30,
1277 'param2' => 2048
1278 ],
1279 'menu' => [
1280 'param1' => "Yes\nNo",
1281 'defaultdata' => 'No'
1282 ],
1283 'datetime' => [
1284 'param1' => '2010',
1285 'param2' => '2015',
1286 'param3' => 1
1287 ],
1288 'checkbox' => [
1289 'defaultdata' => 0
1290 ]
1291 ];
1292 foreach ($typedefaults[$data['datatype']] as $field => $value) {
1293 $defaults[$field] = $value;
1294 }
1295
1296 foreach ($defaults as $field => $value) {
1297 if (!array_key_exists($field, $data)) {
1298 $data[$field] = $value;
1299 }
1300 }
1301
1302 $data['id'] = $DB->insert_record('user_info_field', $data);
1303 return (object)$data;
1304 }
1305
e984917d
AN
1306 /**
1307 * Create a new user, and enrol them in the specified course as the supplied role.
1308 *
1309 * @param \stdClass $course The course to enrol in
1310 * @param string $role The role to give within the course
1311 * @param \stdClass $userparams User parameters
1312 * @return \stdClass The created user
1313 */
1314 public function create_and_enrol($course, $role = 'student', $userparams = null, $enrol = 'manual',
1315 $timestart = 0, $timeend = 0, $status = null) {
1316 global $DB;
1317
1318 $user = $this->create_user($userparams);
1319 $roleid = $DB->get_field('role', 'id', ['shortname' => $role ]);
1320
1321 $this->enrol_user($user->id, $course->id, $roleid, $enrol, $timestart, $timeend, $status);
1322
1323 return $user;
1324 }
4157e2d4
MH
1325
1326 /**
1327 * Create a new last access record for a given user in a course.
1328 *
1329 * @param \stdClass $user The user
1330 * @param \stdClass $course The course the user accessed
1331 * @param int $timestamp The timestamp for when the user last accessed the course
1332 * @return \stdClass The user_lastaccess record
1333 */
1334 public function create_user_course_lastaccess(\stdClass $user, \stdClass $course, int $timestamp): \stdClass {
1335 global $DB;
1336
1337 $record = [
1338 'userid' => $user->id,
1339 'courseid' => $course->id,
1340 'timeaccess' => $timestamp,
1341 ];
1342
1343 $recordid = $DB->insert_record('user_lastaccess', $record);
1344
1345 return $DB->get_record('user_lastaccess', ['id' => $recordid], '*', MUST_EXIST);
1346 }
7e7cfe7a 1347}