Merge branch 'MDL-47408-master' of https://github.com/DmitryNefedov/moodle
[moodle.git] / lib / testing / generator / data_generator.php
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/>.
17 /**
18  * Data generator.
19  *
20  * @package    core
21  * @category   test
22  * @copyright  2012 Petr Skoda {@link http://skodak.org}
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 /**
29  * Data generator class for unit tests and other tools that need to create fake test sites.
30  *
31  * @package    core
32  * @category   test
33  * @copyright  2012 Petr Skoda {@link http://skodak.org}
34  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35  */
36 class testing_data_generator {
37     protected $gradecategorycounter = 0;
38     protected $usercounter = 0;
39     protected $categorycount = 0;
40     protected $cohortcount = 0;
41     protected $coursecount = 0;
42     protected $scalecount = 0;
43     protected $groupcount = 0;
44     protected $groupingcount = 0;
45     protected $rolecount = 0;
46     protected $tagcount = 0;
48     /** @var array list of plugin generators */
49     protected $generators = array();
51     /** @var array lis of common last names */
52     public $lastnames = array(
53         'Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller', 'Davis', 'García', 'Rodríguez', 'Wilson',
54         'Müller', 'Schmidt', 'Schneider', 'Fischer', 'Meyer', 'Weber', 'Schulz', 'Wagner', 'Becker', 'Hoffmann',
55         'Novák', 'Svoboda', 'Novotný', 'Dvořák', 'Černý', 'Procházková', 'Kučerová', 'Veselá', 'Horáková', 'Němcová',
56         'Смирнов', 'Иванов', 'Кузнецов', 'Соколов', 'Попов', 'Лебедева', 'Козлова', 'Новикова', 'Морозова', 'Петрова',
57         '王', '李', '张', '刘', '陈', '楊', '黃', '趙', '吳', '周',
58         '佐藤', '鈴木', '高橋', '田中', '渡辺', '伊藤', '山本', '中村', '小林', '斎藤',
59     );
61     /** @var array lis of common first names */
62     public $firstnames = array(
63         'Jacob', 'Ethan', 'Michael', 'Jayden', 'William', 'Isabella', 'Sophia', 'Emma', 'Olivia', 'Ava',
64         'Lukas', 'Leon', 'Luca', 'Timm', 'Paul', 'Leonie', 'Leah', 'Lena', 'Hanna', 'Laura',
65         'Jakub', 'Jan', 'Tomáš', 'Lukáš', 'Matěj', 'Tereza', 'Eliška', 'Anna', 'Adéla', 'Karolína',
66         'Даниил', 'Максим', 'Артем', 'Иван', 'Александр', 'София', 'Анастасия', 'Дарья', 'Мария', 'Полина',
67         '伟', '伟', '芳', '伟', '秀英', '秀英', '娜', '秀英', '伟', '敏',
68         '翔', '大翔', '拓海', '翔太', '颯太', '陽菜', 'さくら', '美咲', '葵', '美羽',
69     );
71     public $loremipsum = <<<EOD
72 Lorem 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.
73 Temporibus 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.
74 Vivamus 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.
75 Integer 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.
76 In 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.
77 EOD;
79     /**
80      * To be called from data reset code only,
81      * do not use in tests.
82      * @return void
83      */
84     public function reset() {
85         $this->usercounter = 0;
86         $this->categorycount = 0;
87         $this->coursecount = 0;
88         $this->scalecount = 0;
90         foreach ($this->generators as $generator) {
91             $generator->reset();
92         }
93     }
95     /**
96      * Return generator for given plugin or component.
97      * @param string $component the component name, e.g. 'mod_forum' or 'core_question'.
98      * @return component_generator_base or rather an instance of the appropriate subclass.
99      */
100     public function get_plugin_generator($component) {
101         list($type, $plugin) = core_component::normalize_component($component);
102         $cleancomponent = $type . '_' . $plugin;
103         if ($cleancomponent != $component) {
104             debugging("Please specify the component you want a generator for as " .
105                     "{$cleancomponent}, not {$component}.", DEBUG_DEVELOPER);
106             $component = $cleancomponent;
107         }
109         if (isset($this->generators[$component])) {
110             return $this->generators[$component];
111         }
113         $dir = core_component::get_component_directory($component);
114         $lib = $dir . '/tests/generator/lib.php';
115         if (!$dir || !is_readable($lib)) {
116             throw new coding_exception("Component {$component} does not support " .
117                     "generators yet. Missing tests/generator/lib.php.");
118         }
120         include_once($lib);
121         $classname = $component . '_generator';
123         if (!class_exists($classname)) {
124             throw new coding_exception("Component {$component} does not support " .
125                     "data generators yet. Class {$classname} not found.");
126         }
128         $this->generators[$component] = new $classname($this);
129         return $this->generators[$component];
130     }
132     /**
133      * Create a test user
134      * @param array|stdClass $record
135      * @param array $options
136      * @return stdClass user record
137      */
138     public function create_user($record=null, array $options=null) {
139         global $DB, $CFG;
141         $this->usercounter++;
142         $i = $this->usercounter;
144         $record = (array)$record;
146         if (!isset($record['auth'])) {
147             $record['auth'] = 'manual';
148         }
150         if (!isset($record['firstname']) and !isset($record['lastname'])) {
151             $country = rand(0, 5);
152             $firstname = rand(0, 4);
153             $lastname = rand(0, 4);
154             $female = rand(0, 1);
155             $record['firstname'] = $this->firstnames[($country*10) + $firstname + ($female*5)];
156             $record['lastname'] = $this->lastnames[($country*10) + $lastname + ($female*5)];
158         } else if (!isset($record['firstname'])) {
159             $record['firstname'] = 'Firstname'.$i;
161         } else if (!isset($record['lastname'])) {
162             $record['lastname'] = 'Lastname'.$i;
163         }
165         if (!isset($record['firstnamephonetic'])) {
166             $firstnamephonetic = rand(0, 59);
167             $record['firstnamephonetic'] = $this->firstnames[$firstnamephonetic];
168         }
170         if (!isset($record['lasttnamephonetic'])) {
171             $lastnamephonetic = rand(0, 59);
172             $record['lastnamephonetic'] = $this->lastnames[$lastnamephonetic];
173         }
175         if (!isset($record['middlename'])) {
176             $middlename = rand(0, 59);
177             $record['middlename'] = $this->firstnames[$middlename];
178         }
180         if (!isset($record['alternatename'])) {
181             $alternatename = rand(0, 59);
182             $record['alternatename'] = $this->firstnames[$alternatename];
183         }
185         if (!isset($record['idnumber'])) {
186             $record['idnumber'] = '';
187         }
189         if (!isset($record['mnethostid'])) {
190             $record['mnethostid'] = $CFG->mnet_localhost_id;
191         }
193         if (!isset($record['username'])) {
194             $record['username'] = 'username'.$i;
195             $j = 2;
196             while ($DB->record_exists('user', array('username'=>$record['username'], 'mnethostid'=>$record['mnethostid']))) {
197                 $record['username'] = 'username'.$i.'_'.$j;
198                 $j++;
199             }
200         }
202         if (isset($record['password'])) {
203             $record['password'] = hash_internal_user_password($record['password']);
204         } else {
205             // The auth plugin may not fully support this,
206             // but it is still better/faster than hashing random stuff.
207             $record['password'] = AUTH_PASSWORD_NOT_CACHED;
208         }
210         if (!isset($record['email'])) {
211             $record['email'] = $record['username'].'@example.com';
212         }
214         if (!isset($record['confirmed'])) {
215             $record['confirmed'] = 1;
216         }
218         if (!isset($record['lang'])) {
219             $record['lang'] = 'en';
220         }
222         if (!isset($record['maildisplay'])) {
223             $record['maildisplay'] = $CFG->defaultpreference_maildisplay;
224         }
226         if (!isset($record['mailformat'])) {
227             $record['mailformat'] = $CFG->defaultpreference_mailformat;
228         }
230         if (!isset($record['maildigest'])) {
231             $record['maildigest'] = $CFG->defaultpreference_maildigest;
232         }
234         if (!isset($record['autosubscribe'])) {
235             $record['autosubscribe'] = $CFG->defaultpreference_autosubscribe;
236         }
238         if (!isset($record['trackforums'])) {
239             $record['trackforums'] = $CFG->defaultpreference_trackforums;
240         }
242         if (!isset($record['deleted'])) {
243             $record['deleted'] = 0;
244         }
246         if (!isset($record['timecreated'])) {
247             $record['timecreated'] = time();
248         }
250         $record['timemodified'] = $record['timecreated'];
251         $record['lastip'] = '0.0.0.0';
253         if ($record['deleted']) {
254             $delname = $record['email'].'.'.time();
255             while ($DB->record_exists('user', array('username'=>$delname))) {
256                 $delname++;
257             }
258             $record['idnumber'] = '';
259             $record['email']    = md5($record['username']);
260             $record['username'] = $delname;
261             $record['picture']  = 0;
262         }
264         $userid = $DB->insert_record('user', $record);
266         if (!$record['deleted']) {
267             context_user::instance($userid);
268         }
270         return $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
271     }
273     /**
274      * Create a test course category
275      * @param array|stdClass $record
276      * @param array $options
277      * @return coursecat course category record
278      */
279     public function create_category($record=null, array $options=null) {
280         global $DB, $CFG;
281         require_once("$CFG->libdir/coursecatlib.php");
283         $this->categorycount++;
284         $i = $this->categorycount;
286         $record = (array)$record;
288         if (!isset($record['name'])) {
289             $record['name'] = 'Course category '.$i;
290         }
292         if (!isset($record['description'])) {
293             $record['description'] = "Test course category $i\n$this->loremipsum";
294         }
296         if (!isset($record['idnumber'])) {
297             $record['idnumber'] = '';
298         }
300         return coursecat::create($record);
301     }
303     /**
304      * Create test cohort.
305      * @param array|stdClass $record
306      * @param array $options
307      * @return stdClass cohort record
308      */
309     public function create_cohort($record=null, array $options=null) {
310         global $DB, $CFG;
311         require_once("$CFG->dirroot/cohort/lib.php");
313         $this->cohortcount++;
314         $i = $this->cohortcount;
316         $record = (array)$record;
318         if (!isset($record['contextid'])) {
319             $record['contextid'] = context_system::instance()->id;
320         }
322         if (!isset($record['name'])) {
323             $record['name'] = 'Cohort '.$i;
324         }
326         if (!isset($record['idnumber'])) {
327             $record['idnumber'] = '';
328         }
330         if (!isset($record['description'])) {
331             $record['description'] = "Test cohort $i\n$this->loremipsum";
332         }
334         if (!isset($record['descriptionformat'])) {
335             $record['descriptionformat'] = FORMAT_MOODLE;
336         }
338         if (!isset($record['component'])) {
339             $record['component'] = '';
340         }
342         $id = cohort_add_cohort((object)$record);
344         return $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST);
345     }
347     /**
348      * Create a test course
349      * @param array|stdClass $record
350      * @param array $options with keys:
351      *      'createsections'=>bool precreate all sections
352      * @return stdClass course record
353      */
354     public function create_course($record=null, array $options=null) {
355         global $DB, $CFG;
356         require_once("$CFG->dirroot/course/lib.php");
358         $this->coursecount++;
359         $i = $this->coursecount;
361         $record = (array)$record;
363         if (!isset($record['fullname'])) {
364             $record['fullname'] = 'Test course '.$i;
365         }
367         if (!isset($record['shortname'])) {
368             $record['shortname'] = 'tc_'.$i;
369         }
371         if (!isset($record['idnumber'])) {
372             $record['idnumber'] = '';
373         }
375         if (!isset($record['format'])) {
376             $record['format'] = 'topics';
377         }
379         if (!isset($record['newsitems'])) {
380             $record['newsitems'] = 0;
381         }
383         if (!isset($record['numsections'])) {
384             $record['numsections'] = 5;
385         }
387         if (!isset($record['summary'])) {
388             $record['summary'] = "Test course $i\n$this->loremipsum";
389         }
391         if (!isset($record['summaryformat'])) {
392             $record['summaryformat'] = FORMAT_MOODLE;
393         }
395         if (!isset($record['category'])) {
396             $record['category'] = $DB->get_field_select('course_categories', "MIN(id)", "parent=0");
397         }
399         $course = create_course((object)$record);
400         context_course::instance($course->id);
401         if (!empty($options['createsections'])) {
402             if (isset($course->numsections)) {
403                 course_create_sections_if_missing($course, range(0, $course->numsections));
404             } else {
405                 course_create_sections_if_missing($course, 0);
406             }
407         }
409         return $course;
410     }
412     /**
413      * Create course section if does not exist yet
414      * @param array|stdClass $record must contain 'course' and 'section' attributes
415      * @param array|null $options
416      * @return stdClass
417      * @throws coding_exception
418      */
419     public function create_course_section($record = null, array $options = null) {
420         global $DB;
422         $record = (array)$record;
424         if (empty($record['course'])) {
425             throw new coding_exception('course must be present in testing_data_generator::create_course_section() $record');
426         }
428         if (!isset($record['section'])) {
429             throw new coding_exception('section must be present in testing_data_generator::create_course_section() $record');
430         }
432         course_create_sections_if_missing($record['course'], $record['section']);
433         return get_fast_modinfo($record['course'])->get_section_info($record['section']);
434     }
436     /**
437      * Create a test block
438      * @param string $blockname
439      * @param array|stdClass $record
440      * @param array $options
441      * @return stdClass block instance record
442      */
443     public function create_block($blockname, $record=null, array $options=null) {
444         $generator = $this->get_plugin_generator('block_'.$blockname);
445         return $generator->create_instance($record, $options);
446     }
448     /**
449      * Create a test module
450      * @param string $modulename
451      * @param array|stdClass $record
452      * @param array $options
453      * @return stdClass activity record
454      */
455     public function create_module($modulename, $record=null, array $options=null) {
456         $generator = $this->get_plugin_generator('mod_'.$modulename);
457         return $generator->create_instance($record, $options);
458     }
460     /**
461      * Create a test group for the specified course
462      *
463      * $record should be either an array or a stdClass containing infomation about the group to create.
464      * At the very least it needs to contain courseid.
465      * Default values are added for name, description, and descriptionformat if they are not present.
466      *
467      * This function calls groups_create_group() to create the group within the database.
468      * @see groups_create_group
469      * @param array|stdClass $record
470      * @return stdClass group record
471      */
472     public function create_group($record) {
473         global $DB, $CFG;
475         require_once($CFG->dirroot . '/group/lib.php');
477         $this->groupcount++;
478         $i = $this->groupcount;
480         $record = (array)$record;
482         if (empty($record['courseid'])) {
483             throw new coding_exception('courseid must be present in testing_data_generator::create_group() $record');
484         }
486         if (!isset($record['name'])) {
487             $record['name'] = 'group-' . $i;
488         }
490         if (!isset($record['description'])) {
491             $record['description'] = "Test Group $i\n{$this->loremipsum}";
492         }
494         if (!isset($record['descriptionformat'])) {
495             $record['descriptionformat'] = FORMAT_MOODLE;
496         }
498         $id = groups_create_group((object)$record);
500         return $DB->get_record('groups', array('id'=>$id));
501     }
503     /**
504      * Create a test group member
505      * @param array|stdClass $record
506      * @throws coding_exception
507      * @return boolean
508      */
509     public function create_group_member($record) {
510         global $DB, $CFG;
512         require_once($CFG->dirroot . '/group/lib.php');
514         $record = (array)$record;
516         if (empty($record['userid'])) {
517             throw new coding_exception('user must be present in testing_util::create_group_member() $record');
518         }
520         if (!isset($record['groupid'])) {
521             throw new coding_exception('group must be present in testing_util::create_group_member() $record');
522         }
524         if (!isset($record['component'])) {
525             $record['component'] = null;
526         }
527         if (!isset($record['itemid'])) {
528             $record['itemid'] = 0;
529         }
531         return groups_add_member($record['groupid'], $record['userid'], $record['component'], $record['itemid']);
532     }
534     /**
535      * Create a test grouping for the specified course
536      *
537      * $record should be either an array or a stdClass containing infomation about the grouping to create.
538      * At the very least it needs to contain courseid.
539      * Default values are added for name, description, and descriptionformat if they are not present.
540      *
541      * This function calls groups_create_grouping() to create the grouping within the database.
542      * @see groups_create_grouping
543      * @param array|stdClass $record
544      * @return stdClass grouping record
545      */
546     public function create_grouping($record) {
547         global $DB, $CFG;
549         require_once($CFG->dirroot . '/group/lib.php');
551         $this->groupingcount++;
552         $i = $this->groupingcount;
554         $record = (array)$record;
556         if (empty($record['courseid'])) {
557             throw new coding_exception('courseid must be present in testing_data_generator::create_grouping() $record');
558         }
560         if (!isset($record['name'])) {
561             $record['name'] = 'grouping-' . $i;
562         }
564         if (!isset($record['description'])) {
565             $record['description'] = "Test Grouping $i\n{$this->loremipsum}";
566         }
568         if (!isset($record['descriptionformat'])) {
569             $record['descriptionformat'] = FORMAT_MOODLE;
570         }
572         $id = groups_create_grouping((object)$record);
574         return $DB->get_record('groupings', array('id'=>$id));
575     }
577     /**
578      * Create a test grouping group
579      * @param array|stdClass $record
580      * @throws coding_exception
581      * @return boolean
582      */
583     public function create_grouping_group($record) {
584         global $DB, $CFG;
586         require_once($CFG->dirroot . '/group/lib.php');
588         $record = (array)$record;
590         if (empty($record['groupingid'])) {
591             throw new coding_exception('grouping must be present in testing::create_grouping_group() $record');
592         }
594         if (!isset($record['groupid'])) {
595             throw new coding_exception('group must be present in testing_util::create_grouping_group() $record');
596         }
598         return groups_assign_grouping($record['groupingid'], $record['groupid']);
599     }
601     /**
602      * Create an instance of a repository.
603      *
604      * @param string type of repository to create an instance for.
605      * @param array|stdClass $record data to use to up set the instance.
606      * @param array $options options
607      * @return stdClass repository instance record
608      * @since Moodle 2.5.1
609      */
610     public function create_repository($type, $record=null, array $options = null) {
611         $generator = $this->get_plugin_generator('repository_'.$type);
612         return $generator->create_instance($record, $options);
613     }
615     /**
616      * Create an instance of a repository.
617      *
618      * @param string type of repository to create an instance for.
619      * @param array|stdClass $record data to use to up set the instance.
620      * @param array $options options
621      * @return repository_type object
622      * @since Moodle 2.5.1
623      */
624     public function create_repository_type($type, $record=null, array $options = null) {
625         $generator = $this->get_plugin_generator('repository_'.$type);
626         return $generator->create_type($record, $options);
627     }
630     /**
631      * Create a test scale
632      * @param array|stdClass $record
633      * @param array $options
634      * @return stdClass block instance record
635      */
636     public function create_scale($record=null, array $options=null) {
637         global $DB;
639         $this->scalecount++;
640         $i = $this->scalecount;
642         $record = (array)$record;
644         if (!isset($record['name'])) {
645             $record['name'] = 'Test scale '.$i;
646         }
648         if (!isset($record['scale'])) {
649             $record['scale'] = 'A,B,C,D,F';
650         }
652         if (!isset($record['courseid'])) {
653             $record['courseid'] = 0;
654         }
656         if (!isset($record['userid'])) {
657             $record['userid'] = 0;
658         }
660         if (!isset($record['description'])) {
661             $record['description'] = 'Test scale description '.$i;
662         }
664         if (!isset($record['descriptionformat'])) {
665             $record['descriptionformat'] = FORMAT_MOODLE;
666         }
668         $record['timemodified'] = time();
670         if (isset($record['id'])) {
671             $DB->import_record('scale', $record);
672             $DB->get_manager()->reset_sequence('scale');
673             $id = $record['id'];
674         } else {
675             $id = $DB->insert_record('scale', $record);
676         }
678         return $DB->get_record('scale', array('id'=>$id), '*', MUST_EXIST);
679     }
681     /**
682      * Creates a new role in the system.
683      *
684      * You can fill $record with the role 'name',
685      * 'shortname', 'description' and 'archetype'.
686      *
687      * If an archetype is specified it's capabilities,
688      * context where the role can be assigned and
689      * all other properties are copied from the archetype;
690      * if no archetype is specified it will create an
691      * empty role.
692      *
693      * @param array|stdClass $record
694      * @return int The new role id
695      */
696     public function create_role($record=null) {
697         global $DB;
699         $this->rolecount++;
700         $i = $this->rolecount;
702         $record = (array)$record;
704         if (empty($record['shortname'])) {
705             $record['shortname'] = 'role-' . $i;
706         }
708         if (empty($record['name'])) {
709             $record['name'] = 'Test role ' . $i;
710         }
712         if (empty($record['description'])) {
713             $record['description'] = 'Test role ' . $i . ' description';
714         }
716         if (empty($record['archetype'])) {
717             $record['archetype'] = '';
718         } else {
719             $archetypes = get_role_archetypes();
720             if (empty($archetypes[$record['archetype']])) {
721                 throw new coding_exception('\'role\' requires the field \'archetype\' to specify a ' .
722                     'valid archetype shortname (editingteacher, student...)');
723             }
724         }
726         // Creates the role.
727         if (!$newroleid = create_role($record['name'], $record['shortname'], $record['description'], $record['archetype'])) {
728             throw new coding_exception('There was an error creating \'' . $record['shortname'] . '\' role');
729         }
731         // If no archetype was specified we allow it to be added to all contexts,
732         // otherwise we allow it in the archetype contexts.
733         if (!$record['archetype']) {
734             $contextlevels = array_keys(context_helper::get_all_levels());
735         } else {
736             // Copying from the archetype default rol.
737             $archetyperoleid = $DB->get_field(
738                 'role',
739                 'id',
740                 array('shortname' => $record['archetype'], 'archetype' => $record['archetype'])
741             );
742             $contextlevels = get_role_contextlevels($archetyperoleid);
743         }
744         set_role_contextlevels($newroleid, $contextlevels);
746         if ($record['archetype']) {
748             // We copy all the roles the archetype can assign, override and switch to.
749             if ($record['archetype']) {
750                 $types = array('assign', 'override', 'switch');
751                 foreach ($types as $type) {
752                     $rolestocopy = get_default_role_archetype_allows($type, $record['archetype']);
753                     foreach ($rolestocopy as $tocopy) {
754                         $functionname = 'allow_' . $type;
755                         $functionname($newroleid, $tocopy);
756                     }
757                 }
758             }
760             // Copying the archetype capabilities.
761             $sourcerole = $DB->get_record('role', array('id' => $archetyperoleid));
762             role_cap_duplicate($sourcerole, $newroleid);
763         }
765         return $newroleid;
766     }
768     /**
769      * Create a tag.
770      *
771      * @param array|stdClass $record
772      * @return stdClass the tag record
773      */
774     public function create_tag($record = null) {
775         global $DB, $USER;
777         $this->tagcount++;
778         $i = $this->tagcount;
780         $record = (array) $record;
782         if (!isset($record['userid'])) {
783             $record['userid'] = $USER->id;
784         }
786         if (!isset($record['name'])) {
787             $record['name'] = 'Tag name ' . $i;
788         }
790         if (!isset($record['rawname'])) {
791             $record['rawname'] = 'Raw tag name ' . $i;
792         }
794         if (!isset($record['tagtype'])) {
795             $record['tagtype'] = 'default';
796         }
798         if (!isset($record['description'])) {
799             $record['description'] = 'Tag description';
800         }
802         if (!isset($record['descriptionformat'])) {
803             $record['descriptionformat'] = FORMAT_MOODLE;
804         }
806         if (!isset($record['flag'])) {
807             $record['flag'] = 0;
808         }
810         if (!isset($record['timemodified'])) {
811             $record['timemodified'] = time();
812         }
814         $id = $DB->insert_record('tag', $record);
816         return $DB->get_record('tag', array('id' => $id), '*', MUST_EXIST);
817     }
819     /**
820      * Helper method which combines $defaults with the values specified in $record.
821      * If $record is an object, it is converted to an array.
822      * Then, for each key that is in $defaults, but not in $record, the value
823      * from $defaults is copied.
824      * @param array $defaults the default value for each field with
825      * @param array|stdClass $record
826      * @return array updated $record.
827      */
828     public function combine_defaults_and_record(array $defaults, $record) {
829         $record = (array) $record;
831         foreach ($defaults as $key => $defaults) {
832             if (!array_key_exists($key, $record)) {
833                 $record[$key] = $defaults;
834             }
835         }
836         return $record;
837     }
839     /**
840      * Simplified enrolment of user to course using default options.
841      *
842      * It is strongly recommended to use only this method for 'manual' and 'self' plugins only!!!
843      *
844      * @param int $userid
845      * @param int $courseid
846      * @param int $roleid optional role id, use only with manual plugin
847      * @param string $enrol name of enrol plugin,
848      *     there must be exactly one instance in course,
849      *     it must support enrol_user() method.
850      * @param int $timestart (optional) 0 means unknown
851      * @param int $timeend (optional) 0 means forever
852      * @param int $status (optional) default to ENROL_USER_ACTIVE for new enrolments
853      * @return bool success
854      */
855     public function enrol_user($userid, $courseid, $roleid = null, $enrol = 'manual', $timestart = 0, $timeend = 0, $status = null) {
856         global $DB;
858         if (!$plugin = enrol_get_plugin($enrol)) {
859             return false;
860         }
862         $instances = $DB->get_records('enrol', array('courseid'=>$courseid, 'enrol'=>$enrol));
863         if (count($instances) != 1) {
864             return false;
865         }
866         $instance = reset($instances);
868         if (is_null($roleid) and $instance->roleid) {
869             $roleid = $instance->roleid;
870         }
872         $plugin->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status);
873         return true;
874     }
876     /**
877      * Assigns the specified role to a user in the context.
878      *
879      * @param int $roleid
880      * @param int $userid
881      * @param int $contextid Defaults to the system context
882      * @return int new/existing id of the assignment
883      */
884     public function role_assign($roleid, $userid, $contextid = false) {
886         // Default to the system context.
887         if (!$contextid) {
888             $context = context_system::instance();
889             $contextid = $context->id;
890         }
892         if (empty($roleid)) {
893             throw new coding_exception('roleid must be present in testing_data_generator::role_assign() arguments');
894         }
896         if (empty($userid)) {
897             throw new coding_exception('userid must be present in testing_data_generator::role_assign() arguments');
898         }
900         return role_assign($roleid, $userid, $contextid);
901     }
903     /**
904      * Create a grade_category.
905      *
906      * @param array|stdClass $record
907      * @return stdClass the grade category record
908      */
909     public function create_grade_category($record = null) {
910         global $CFG;
912         $this->gradecategorycounter++;
913         $i = $this->gradecategorycounter;
915         if (!isset($record['fullname'])) {
916             $record['fullname'] = 'Grade category ' . $i;
917         }
919         // For gradelib classes.
920         require_once($CFG->libdir . '/gradelib.php');
921         // Create new grading category in this course.
922         $gradecategory = new grade_category($record, false);
923         $gradecategory->apply_default_settings();
924         $gradecategory->apply_forced_settings();
925         $gradecategory->insert();
926         // This creates a default grade item for the category
927         $gradeitem = $gradecategory->load_grade_item();
929         if (isset($record->parentcategory)) {
930             $gradecategory->set_parent($data->parentcategory);
931         }
933         $gradecategory->update_from_db();
934         return $gradecategory->get_record_data();
935     }