MDL-68612 user: Fixed delete participants filter row execution order
[moodle.git] / lib / behat / classes / behat_core_generator.php
CommitLineData
285c7036
TH
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 generators for acceptance testing.
19 *
20 * @package core
21 * @category test
22 * @copyright 2012 David MonllaĆ³
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
27
28defined('MOODLE_INTERNAL') || die();
29
30
31/**
32 * Behat data generator class for core entities.
33 *
34 * @package core
35 * @category test
36 * @copyright 2012 David MonllaĆ³
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 */
39class behat_core_generator extends behat_generator_base {
40
41 protected function get_creatable_entities(): array {
42 return [
43 'users' => [
44 'datagenerator' => 'user',
45 'required' => ['username'],
46 ],
47 'categories' => [
48 'datagenerator' => 'category',
49 'required' => ['idnumber'],
50 'switchids' => ['category' => 'parent'],
51 ],
52 'courses' => [
53 'datagenerator' => 'course',
54 'required' => ['shortname'],
55 'switchids' => ['category' => 'category'],
56 ],
57 'groups' => [
58 'datagenerator' => 'group',
59 'required' => ['idnumber', 'course'],
60 'switchids' => ['course' => 'courseid'],
61 ],
62 'groupings' => [
63 'datagenerator' => 'grouping',
64 'required' => ['idnumber', 'course'],
65 'switchids' => ['course' => 'courseid'],
66 ],
67 'course enrolments' => [
68 'datagenerator' => 'enrol_user',
69 'required' => ['user', 'course', 'role'],
70 'switchids' => ['user' => 'userid', 'course' => 'courseid', 'role' => 'roleid'],
71 ],
72 'custom field categories' => [
73 'datagenerator' => 'custom_field_category',
74 'required' => ['name', 'component', 'area', 'itemid'],
75 'switchids' => [],
76 ],
77 'custom fields' => [
78 'datagenerator' => 'custom_field',
79 'required' => ['name', 'category', 'type', 'shortname'],
80 'switchids' => [],
81 ],
82 'permission overrides' => [
83 'datagenerator' => 'permission_override',
84 'required' => ['capability', 'permission', 'role', 'contextlevel', 'reference'],
85 'switchids' => ['role' => 'roleid'],
86 ],
87 'system role assigns' => [
88 'datagenerator' => 'system_role_assign',
89 'required' => ['user', 'role'],
90 'switchids' => ['user' => 'userid', 'role' => 'roleid'],
91 ],
92 'role assigns' => [
93 'datagenerator' => 'role_assign',
94 'required' => ['user', 'role', 'contextlevel', 'reference'],
95 'switchids' => ['user' => 'userid', 'role' => 'roleid'],
96 ],
97 'activities' => [
98 'datagenerator' => 'activity',
99 'required' => ['activity', 'idnumber', 'course'],
100 'switchids' => ['course' => 'course', 'gradecategory' => 'gradecat', 'grouping' => 'groupingid'],
101 ],
102 'blocks' => [
103 'datagenerator' => 'block_instance',
104 'required' => ['blockname', 'contextlevel', 'reference'],
105 ],
106 'group members' => [
107 'datagenerator' => 'group_member',
108 'required' => ['user', 'group'],
109 'switchids' => ['user' => 'userid', 'group' => 'groupid'],
110 ],
111 'grouping groups' => [
112 'datagenerator' => 'grouping_group',
113 'required' => ['grouping', 'group'],
114 'switchids' => ['grouping' => 'groupingid', 'group' => 'groupid'],
115 ],
116 'cohorts' => [
117 'datagenerator' => 'cohort',
118 'required' => ['idnumber'],
119 ],
120 'cohort members' => [
121 'datagenerator' => 'cohort_member',
122 'required' => ['user', 'cohort'],
123 'switchids' => ['user' => 'userid', 'cohort' => 'cohortid'],
124 ],
125 'roles' => [
126 'datagenerator' => 'role',
127 'required' => ['shortname'],
128 ],
129 'grade categories' => [
130 'datagenerator' => 'grade_category',
131 'required' => ['fullname', 'course'],
132 'switchids' => ['course' => 'courseid', 'gradecategory' => 'parent'],
133 ],
134 'grade items' => [
135 'datagenerator' => 'grade_item',
136 'required' => ['course'],
137 'switchids' => [
138 'scale' => 'scaleid',
139 'outcome' => 'outcomeid',
140 'course' => 'courseid',
141 'gradecategory' => 'categoryid',
142 ],
143 ],
144 'grade outcomes' => [
145 'datagenerator' => 'grade_outcome',
146 'required' => ['shortname', 'scale'],
147 'switchids' => ['course' => 'courseid', 'gradecategory' => 'categoryid', 'scale' => 'scaleid'],
148 ],
149 'scales' => [
150 'datagenerator' => 'scale',
151 'required' => ['name', 'scale'],
152 'switchids' => ['course' => 'courseid'],
153 ],
154 'question categories' => [
155 'datagenerator' => 'question_category',
156 'required' => ['name', 'contextlevel', 'reference'],
157 'switchids' => ['questioncategory' => 'parent'],
158 ],
159 'questions' => [
160 'datagenerator' => 'question',
161 'required' => ['qtype', 'questioncategory', 'name'],
162 'switchids' => ['questioncategory' => 'category', 'user' => 'createdby'],
163 ],
164 'tags' => [
165 'datagenerator' => 'tag',
166 'required' => ['name'],
167 ],
168 'events' => [
169 'datagenerator' => 'event',
170 'required' => ['name', 'eventtype'],
171 'switchids' => [
172 'user' => 'userid',
173 'course' => 'courseid',
174 'category' => 'categoryid',
175 ],
176 ],
177 'message contacts' => [
178 'datagenerator' => 'message_contacts',
179 'required' => ['user', 'contact'],
180 'switchids' => ['user' => 'userid', 'contact' => 'contactid'],
181 ],
182 'private messages' => [
183 'datagenerator' => 'private_messages',
184 'required' => ['user', 'contact', 'message'],
185 'switchids' => ['user' => 'userid', 'contact' => 'contactid'],
186 ],
187 'favourite conversations' => [
188 'datagenerator' => 'favourite_conversations',
189 'required' => ['user', 'contact'],
190 'switchids' => ['user' => 'userid', 'contact' => 'contactid'],
191 ],
192 'group messages' => [
193 'datagenerator' => 'group_messages',
194 'required' => ['user', 'group', 'message'],
195 'switchids' => ['user' => 'userid', 'group' => 'groupid'],
196 ],
197 'muted group conversations' => [
198 'datagenerator' => 'mute_group_conversations',
199 'required' => ['user', 'group', 'course'],
200 'switchids' => ['user' => 'userid', 'group' => 'groupid', 'course' => 'courseid'],
201 ],
202 'muted private conversations' => [
203 'datagenerator' => 'mute_private_conversations',
204 'required' => ['user', 'contact'],
205 'switchids' => ['user' => 'userid', 'contact' => 'contactid'],
206 ],
207 'language customisations' => [
208 'datagenerator' => 'customlang',
209 'required' => ['component', 'stringid', 'value'],
210 ],
211 'analytics model' => [
212 'datagenerator' => 'analytics_model',
213 'required' => ['target', 'indicators', 'timesplitting', 'enabled'],
214 ],
ebe53aca
AA
215 'user preferences' => [
216 'datagenerator' => 'user_preferences',
217 'required' => array('user', 'preference', 'value'),
218 'switchids' => array('user' => 'userid')
219 ],
cc58791b
AA
220 'contentbank content' => [
221 'datagenerator' => 'contentbank_content',
2e4e031e
MG
222 'required' => array('contextlevel', 'reference', 'contenttype', 'user', 'contentname'),
223 'switchids' => array('user' => 'userid')
cc58791b 224 ],
15a00bea
TT
225 'badge external backpack' => [
226 'datagenerator' => 'badge_external_backpack',
227 'required' => ['backpackapiurl', 'backpackweburl', 'apiversion']
228 ],
229 'setup backpack connected' => [
230 'datagenerator' => 'setup_backpack_connected',
231 'required' => ['user', 'externalbackpack'],
232 'switchids' => ['user' => 'userid', 'externalbackpack' => 'externalbackpackid']
233 ]
285c7036
TH
234 ];
235 }
236
237 /**
238 * Remove any empty custom fields, to avoid errors when creating the course.
239 *
240 * @param array $data
241 * @return array
242 */
243 protected function preprocess_course($data) {
244 foreach ($data as $fieldname => $value) {
245 if ($value === '' && strpos($fieldname, 'customfield_') === 0) {
246 unset($data[$fieldname]);
247 }
248 }
249 return $data;
250 }
251
252 /**
253 * If password is not set it uses the username.
254 *
255 * @param array $data
256 * @return array
257 */
258 protected function preprocess_user($data) {
259 if (!isset($data['password'])) {
260 $data['password'] = $data['username'];
261 }
262 return $data;
263 }
264
265 /**
266 * If contextlevel and reference are specified for cohort, transform them to the contextid.
267 *
268 * @param array $data
269 * @return array
270 */
271 protected function preprocess_cohort($data) {
272 if (isset($data['contextlevel'])) {
273 if (!isset($data['reference'])) {
274 throw new Exception('If field contextlevel is specified, field reference must also be present');
275 }
276 $context = $this->get_context($data['contextlevel'], $data['reference']);
277 unset($data['contextlevel']);
278 unset($data['reference']);
279 $data['contextid'] = $context->id;
280 }
281 return $data;
282 }
283
284 /**
285 * Preprocesses the creation of a grade item. Converts gradetype text to a number.
286 *
287 * @param array $data
288 * @return array
289 */
290 protected function preprocess_grade_item($data) {
291 global $CFG;
292 require_once("$CFG->libdir/grade/constants.php");
293
294 if (isset($data['gradetype'])) {
295 $data['gradetype'] = constant("GRADE_TYPE_" . strtoupper($data['gradetype']));
296 }
297
298 if (!empty($data['category']) && !empty($data['courseid'])) {
299 $cat = grade_category::fetch(array('fullname' => $data['category'], 'courseid' => $data['courseid']));
300 if (!$cat) {
301 throw new Exception('Could not resolve category with name "' . $data['category'] . '"');
302 }
303 unset($data['category']);
304 $data['categoryid'] = $cat->id;
305 }
306
307 return $data;
308 }
309
310 /**
311 * Adapter to modules generator.
312 *
313 * @throws Exception Custom exception for test writers
314 * @param array $data
315 * @return void
316 */
317 protected function process_activity($data) {
318 global $DB, $CFG;
319
320 // The the_following_exists() method checks that the field exists.
321 $activityname = $data['activity'];
322 unset($data['activity']);
323
324 // Convert scale name into scale id (negative number indicates using scale).
325 if (isset($data['grade']) && strlen($data['grade']) && !is_number($data['grade'])) {
326 $data['grade'] = - $this->get_scale_id($data['grade']);
327 require_once("$CFG->libdir/grade/constants.php");
328
329 if (!isset($data['gradetype'])) {
330 $data['gradetype'] = GRADE_TYPE_SCALE;
331 }
332 }
333
334 // We split $data in the activity $record and the course module $options.
335 $cmoptions = array();
336 $cmcolumns = $DB->get_columns('course_modules');
337 foreach ($cmcolumns as $key => $value) {
338 if (isset($data[$key])) {
339 $cmoptions[$key] = $data[$key];
340 }
341 }
342
343 // Custom exception.
344 try {
345 $this->datagenerator->create_module($activityname, $data, $cmoptions);
346 } catch (coding_exception $e) {
347 throw new Exception('\'' . $activityname . '\' activity can not be added using this step,' .
348 ' use the step \'I add a "ACTIVITY_OR_RESOURCE_NAME_STRING" to section "SECTION_NUMBER"\' instead');
349 }
350 }
351
352 /**
353 * Add a block to a page.
354 *
355 * @param array $data should mostly match the fields of the block_instances table.
356 * The block type is specified by blockname.
357 * The parentcontextid is set from contextlevel and reference.
358 * Missing values are filled in by testing_block_generator::prepare_record.
359 * $data is passed to create_block as both $record and $options. Normally
360 * the keys are different, so this is a way to let people set values in either place.
361 */
362 protected function process_block_instance($data) {
363
364 if (empty($data['blockname'])) {
365 throw new Exception('\'blocks\' requires the field \'block\' type to be specified');
366 }
367
368 if (empty($data['contextlevel'])) {
369 throw new Exception('\'blocks\' requires the field \'contextlevel\' to be specified');
370 }
371
372 if (!isset($data['reference'])) {
373 throw new Exception('\'blocks\' requires the field \'reference\' to be specified');
374 }
375
376 $context = $this->get_context($data['contextlevel'], $data['reference']);
377 $data['parentcontextid'] = $context->id;
378
379 // Pass $data as both $record and $options. I think that is unlikely to
380 // cause problems since the relevant key names are different.
381 // $options is not used in most blocks I have seen, but where it is, it is necessary.
382 $this->datagenerator->create_block($data['blockname'], $data, $data);
383 }
384
385 /**
386 * Creates language customisation.
387 *
388 * @throws Exception
389 * @throws dml_exception
390 * @param array $data
391 * @return void
392 */
393 protected function process_customlang($data) {
394 global $CFG, $DB, $USER;
395
396 require_once($CFG->dirroot . '/' . $CFG->admin . '/tool/customlang/locallib.php');
397 require_once($CFG->libdir . '/adminlib.php');
398
399 if (empty($data['component'])) {
400 throw new Exception('\'customlang\' requires the field \'component\' type to be specified');
401 }
402
403 if (empty($data['stringid'])) {
404 throw new Exception('\'customlang\' requires the field \'stringid\' to be specified');
405 }
406
407 if (!isset($data['value'])) {
408 throw new Exception('\'customlang\' requires the field \'value\' to be specified');
409 }
410
411 $now = time();
412
413 tool_customlang_utils::checkout($USER->lang);
414
415 $record = $DB->get_record_sql("SELECT s.*
416 FROM {tool_customlang} s
417 JOIN {tool_customlang_components} c ON s.componentid = c.id
418 WHERE c.name = ? AND s.lang = ? AND s.stringid = ?",
419 array($data['component'], $USER->lang, $data['stringid']));
420
421 if (empty($data['value']) && !is_null($record->local)) {
422 $record->local = null;
423 $record->modified = 1;
424 $record->outdated = 0;
425 $record->timecustomized = null;
426 $DB->update_record('tool_customlang', $record);
427 tool_customlang_utils::checkin($USER->lang);
428 }
429
430 if (!empty($data['value']) && $data['value'] != $record->local) {
431 $record->local = $data['value'];
432 $record->modified = 1;
433 $record->outdated = 0;
434 $record->timecustomized = $now;
435 $DB->update_record('tool_customlang', $record);
436 tool_customlang_utils::checkin($USER->lang);
437 }
438 }
439
440 /**
441 * Adapter to enrol_user() data generator.
442 *
443 * @throws Exception
444 * @param array $data
445 * @return void
446 */
447 protected function process_enrol_user($data) {
448 global $SITE;
449
450 if (empty($data['roleid'])) {
451 throw new Exception('\'course enrolments\' requires the field \'role\' to be specified');
452 }
453
454 if (!isset($data['userid'])) {
455 throw new Exception('\'course enrolments\' requires the field \'user\' to be specified');
456 }
457
458 if (!isset($data['courseid'])) {
459 throw new Exception('\'course enrolments\' requires the field \'course\' to be specified');
460 }
461
462 if (!isset($data['enrol'])) {
463 $data['enrol'] = 'manual';
464 }
465
466 if (!isset($data['timestart'])) {
467 $data['timestart'] = 0;
468 }
469
470 if (!isset($data['timeend'])) {
471 $data['timeend'] = 0;
472 }
473
474 if (!isset($data['status'])) {
475 $data['status'] = null;
476 }
477
478 // If the provided course shortname is the site shortname we consider it a system role assign.
479 if ($data['courseid'] == $SITE->id) {
480 // Frontpage course assign.
481 $context = context_course::instance($data['courseid']);
482 role_assign($data['roleid'], $data['userid'], $context->id);
483
484 } else {
485 // Course assign.
486 $this->datagenerator->enrol_user($data['userid'], $data['courseid'], $data['roleid'], $data['enrol'],
487 $data['timestart'], $data['timeend'], $data['status']);
488 }
489
490 }
491
492 /**
493 * Allows/denies a capability at the specified context
494 *
495 * @throws Exception
496 * @param array $data
497 * @return void
498 */
499 protected function process_permission_override($data) {
500
501 // Will throw an exception if it does not exist.
502 $context = $this->get_context($data['contextlevel'], $data['reference']);
503
504 switch ($data['permission']) {
505 case get_string('allow', 'role'):
506 $permission = CAP_ALLOW;
507 break;
508 case get_string('prevent', 'role'):
509 $permission = CAP_PREVENT;
510 break;
511 case get_string('prohibit', 'role'):
512 $permission = CAP_PROHIBIT;
513 break;
514 default:
515 throw new Exception('The \'' . $data['permission'] . '\' permission does not exist');
516 break;
517 }
518
519 if (is_null(get_capability_info($data['capability']))) {
520 throw new Exception('The \'' . $data['capability'] . '\' capability does not exist');
521 }
522
523 role_change_permission($data['roleid'], $context, $data['capability'], $permission);
524 }
525
526 /**
527 * Assigns a role to a user at system context
528 *
529 * Used by "system role assigns" can be deleted when
530 * system role assign will be deprecated in favour of
531 * "role assigns"
532 *
533 * @throws Exception
534 * @param array $data
535 * @return void
536 */
537 protected function process_system_role_assign($data) {
538
539 if (empty($data['roleid'])) {
540 throw new Exception('\'system role assigns\' requires the field \'role\' to be specified');
541 }
542
543 if (!isset($data['userid'])) {
544 throw new Exception('\'system role assigns\' requires the field \'user\' to be specified');
545 }
546
547 $context = context_system::instance();
548
549 $this->datagenerator->role_assign($data['roleid'], $data['userid'], $context->id);
550 }
551
552 /**
553 * Assigns a role to a user at the specified context
554 *
555 * @throws Exception
556 * @param array $data
557 * @return void
558 */
559 protected function process_role_assign($data) {
560
561 if (empty($data['roleid'])) {
562 throw new Exception('\'role assigns\' requires the field \'role\' to be specified');
563 }
564
565 if (!isset($data['userid'])) {
566 throw new Exception('\'role assigns\' requires the field \'user\' to be specified');
567 }
568
569 if (empty($data['contextlevel'])) {
570 throw new Exception('\'role assigns\' requires the field \'contextlevel\' to be specified');
571 }
572
573 if (!isset($data['reference'])) {
574 throw new Exception('\'role assigns\' requires the field \'reference\' to be specified');
575 }
576
577 // Getting the context id.
578 $context = $this->get_context($data['contextlevel'], $data['reference']);
579
580 $this->datagenerator->role_assign($data['roleid'], $data['userid'], $context->id);
581 }
582
583 /**
584 * Creates a role.
585 *
586 * @param array $data
587 * @return void
588 */
589 protected function process_role($data) {
590
591 // We require the user to fill the role shortname.
592 if (empty($data['shortname'])) {
593 throw new Exception('\'role\' requires the field \'shortname\' to be specified');
594 }
595
596 $this->datagenerator->create_role($data);
597 }
598
599 /**
600 * Adds members to cohorts
601 *
602 * @param array $data
603 * @return void
604 */
605 protected function process_cohort_member($data) {
606 cohort_add_member($data['cohortid'], $data['userid']);
607 }
608
609 /**
610 * Create a question category.
611 *
612 * @param array $data the row of data from the behat script.
613 */
614 protected function process_question_category($data) {
615 global $DB;
616
617 $context = $this->get_context($data['contextlevel'], $data['reference']);
618
619 // The way this class works, we have already looked up the given parent category
620 // name and found a matching category. However, it is possible, particularly
621 // for the 'top' category, for there to be several categories with the
622 // same name. So far one will have been picked at random, but we need
623 // the one from the right context. So, if we have the wrong category, try again.
624 // (Just fixing it here, rather than getting it right first time, is a bit
625 // of a bodge, but in general this class assumes that names are unique,
626 // and normally they are, so this was the easiest fix.)
627 if (!empty($data['parent'])) {
628 $foundparent = $DB->get_record('question_categories', ['id' => $data['parent']], '*', MUST_EXIST);
629 if ($foundparent->contextid != $context->id) {
630 $rightparentid = $DB->get_field('question_categories', 'id',
631 ['contextid' => $context->id, 'name' => $foundparent->name]);
632 if (!$rightparentid) {
633 throw new Exception('The specified question category with name "' . $foundparent->name .
634 '" does not exist in context "' . $context->get_context_name() . '"."');
635 }
636 $data['parent'] = $rightparentid;
637 }
638 }
639
640 $data['contextid'] = $context->id;
641 $this->datagenerator->get_plugin_generator('core_question')->create_question_category($data);
642 }
643
644 /**
645 * Create a question.
646 *
647 * Creating questions relies on the question/type/.../tests/helper.php mechanism.
648 * We start with test_question_maker::get_question_form_data($data['qtype'], $data['template'])
649 * and then overlay the values from any other fields of $data that are set.
650 *
fa7431ce
TH
651 * There is a special case that allows you to set qtype to 'missingtype'.
652 * This creates an example of broken question, such as you might get if you
653 * install a question type, create some questions of that type, and then
654 * uninstall the question type (which is prevented through the UI but can
655 * still happen). This special lets tests verify that these questions are
656 * handled OK.
657 *
285c7036
TH
658 * @param array $data the row of data from the behat script.
659 */
660 protected function process_question($data) {
fa7431ce
TH
661 global $DB;
662
285c7036
TH
663 if (array_key_exists('questiontext', $data)) {
664 $data['questiontext'] = array(
665 'text' => $data['questiontext'],
666 'format' => FORMAT_HTML,
667 );
668 }
669
670 if (array_key_exists('generalfeedback', $data)) {
671 $data['generalfeedback'] = array(
672 'text' => $data['generalfeedback'],
673 'format' => FORMAT_HTML,
674 );
675 }
676
677 $which = null;
678 if (!empty($data['template'])) {
679 $which = $data['template'];
680 }
681
fa7431ce
TH
682 $missingtypespecialcase = false;
683 if ($data['qtype'] === 'missingtype') {
684 $data['qtype'] = 'essay'; // Actual type uses here does not matter. We just need any question.
685 $missingtypespecialcase = true;
686 }
687
688 $questiondata = $this->datagenerator->get_plugin_generator('core_question')
689 ->create_question($data['qtype'], $which, $data);
690
691 if ($missingtypespecialcase) {
692 $DB->set_field('question', 'qtype', 'unknownqtype', ['id' => $questiondata->id]);
693 }
285c7036
TH
694 }
695
696 /**
697 * Adds user to contacts
698 *
699 * @param array $data
700 * @return void
701 */
702 protected function process_message_contacts($data) {
703 \core_message\api::add_contact($data['userid'], $data['contactid']);
704 }
705
706 /**
707 * Send a new message from user to contact in a private conversation
708 *
709 * @param array $data
710 * @return void
711 */
712 protected function process_private_messages(array $data) {
713 if (empty($data['format'])) {
714 $data['format'] = 'FORMAT_PLAIN';
715 }
716
717 if (!$conversationid = \core_message\api::get_conversation_between_users([$data['userid'], $data['contactid']])) {
718 $conversation = \core_message\api::create_conversation(
719 \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
720 [$data['userid'], $data['contactid']]
721 );
722 $conversationid = $conversation->id;
723 }
724 \core_message\api::send_message_to_conversation(
725 $data['userid'],
726 $conversationid,
727 $data['message'],
728 constant($data['format'])
729 );
730 }
731
732 /**
733 * Send a new message from user to a group conversation
734 *
735 * @param array $data
736 * @return void
737 */
738 protected function process_group_messages(array $data) {
739 global $DB;
740
741 if (empty($data['format'])) {
742 $data['format'] = 'FORMAT_PLAIN';
743 }
744
745 $group = $DB->get_record('groups', ['id' => $data['groupid']]);
746 $coursecontext = context_course::instance($group->courseid);
747 if (!$conversation = \core_message\api::get_conversation_by_area('core_group', 'groups', $data['groupid'],
748 $coursecontext->id)) {
749 $members = $DB->get_records_menu('groups_members', ['groupid' => $data['groupid']], '', 'userid, id');
750 $conversation = \core_message\api::create_conversation(
751 \core_message\api::MESSAGE_CONVERSATION_TYPE_GROUP,
752 array_keys($members),
753 $group->name,
754 \core_message\api::MESSAGE_CONVERSATION_ENABLED,
755 'core_group',
756 'groups',
757 $group->id,
758 $coursecontext->id);
759 }
760 \core_message\api::send_message_to_conversation(
761 $data['userid'],
762 $conversation->id,
763 $data['message'],
764 constant($data['format'])
765 );
766 }
767
768 /**
769 * Mark a private conversation as favourite for user
770 *
771 * @param array $data
772 * @return void
773 */
774 protected function process_favourite_conversations(array $data) {
775 if (!$conversationid = \core_message\api::get_conversation_between_users([$data['userid'], $data['contactid']])) {
776 $conversation = \core_message\api::create_conversation(
777 \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
778 [$data['userid'], $data['contactid']]
779 );
780 $conversationid = $conversation->id;
781 }
782 \core_message\api::set_favourite_conversation($conversationid, $data['userid']);
783 }
784
785 /**
786 * Mute an existing group conversation for user
787 *
788 * @param array $data
789 * @return void
790 */
791 protected function process_mute_group_conversations(array $data) {
792 if (groups_is_member($data['groupid'], $data['userid'])) {
793 $context = context_course::instance($data['courseid']);
794 $conversation = \core_message\api::get_conversation_by_area(
795 'core_group',
796 'groups',
797 $data['groupid'],
798 $context->id
799 );
800 if ($conversation) {
801 \core_message\api::mute_conversation($data['userid'], $conversation->id);
802 }
803 }
804 }
805
806 /**
807 * Mute a private conversation for user
808 *
809 * @param array $data
810 * @return void
811 */
812 protected function process_mute_private_conversations(array $data) {
813 if (!$conversationid = \core_message\api::get_conversation_between_users([$data['userid'], $data['contactid']])) {
814 $conversation = \core_message\api::create_conversation(
815 \core_message\api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL,
816 [$data['userid'], $data['contactid']]
817 );
818 $conversationid = $conversation->id;
819 }
820 \core_message\api::mute_conversation($data['userid'], $conversationid);
821 }
822
823 /**
824 * Transform indicators string into array.
825 *
826 * @param array $data
827 * @return array
828 */
829 protected function preprocess_analytics_model($data) {
830 $data['indicators'] = explode(',', $data['indicators']);
831 return $data;
832 }
833
834 /**
835 * Creates an analytics model
836 *
837 * @param target $data
838 * @return void
839 */
840 protected function process_analytics_model($data) {
841 \core_analytics\manager::create_declared_model($data);
842 }
ebe53aca
AA
843
844 /**
845 * Set a preference value for user
846 *
847 * @param array $data
848 * @return void
849 */
850 protected function process_user_preferences(array $data) {
851 set_user_preference($data['preference'], $data['value'], $data['userid']);
852 }
cc58791b
AA
853
854 /**
2e4e031e 855 * Create content in the given context's content bank.
cc58791b
AA
856 *
857 * @param array $data
858 * @return void
859 */
860 protected function process_contentbank_content(array $data) {
2e4e031e
MG
861 global $CFG;
862
863 if (empty($data['contextlevel'])) {
864 throw new Exception('contentbank_content requires the field contextlevel to be specified');
ce1fe72d 865 }
2e4e031e
MG
866
867 if (!isset($data['reference'])) {
868 throw new Exception('contentbank_content requires the field reference to be specified');
869 }
870
871 if (empty($data['contenttype'])) {
872 throw new Exception('contentbank_content requires the field contenttype to be specified');
873 }
874
cc58791b
AA
875 $contenttypeclass = "\\".$data['contenttype']."\\contenttype";
876 if (class_exists($contenttypeclass)) {
2e4e031e 877 $context = $this->get_context($data['contextlevel'], $data['reference']);
cc58791b
AA
878 $contenttype = new $contenttypeclass($context);
879 $record = new stdClass();
880 $record->usercreated = $data['userid'];
881 $record->name = $data['contentname'];
882 $content = $contenttype->create_content($record);
2e4e031e
MG
883
884 if (!empty($data['filepath'])) {
f9d2635f 885 $filename = basename($data['filepath']);
2e4e031e
MG
886 $fs = get_file_storage();
887 $filerecord = array(
888 'component' => 'contentbank',
889 'filearea' => 'public',
890 'contextid' => $context->id,
891 'userid' => $data['userid'],
892 'itemid' => $content->get_id(),
f9d2635f 893 'filename' => $filename,
2e4e031e
MG
894 'filepath' => '/'
895 );
896 $fs->create_file_from_pathname($filerecord, $CFG->dirroot . $data['filepath']);
897 }
898 } else {
899 throw new Exception('The specified "' . $data['contenttype'] . '" contenttype does not exist');
cc58791b
AA
900 }
901 }
15a00bea
TT
902
903 /**
904 * Create a exetrnal backpack.
905 *
906 * @param array $data
907 */
908 protected function process_badge_external_backpack(array $data) {
909 global $DB;
910 $DB->insert_record('badge_external_backpack', $data, true);
911 }
912
913 /**
914 * Setup a backpack connected for user.
915 *
916 * @param array $data
917 * @throws dml_exception
918 */
919 protected function process_setup_backpack_connected(array $data) {
920 global $DB;
921
922 if (empty($data['userid'])) {
923 throw new Exception('\'setup backpack connected\' requires the field \'user\' to be specified');
924 }
925 if (empty($data['externalbackpackid'])) {
926 throw new Exception('\'setup backpack connected\' requires the field \'externalbackpack\' to be specified');
927 }
928 // Dummy badge_backpack_oauth2 data.
929 $timenow = time();
930 $backpackoauth2 = new stdClass();
931 $backpackoauth2->usermodified = $data['userid'];
932 $backpackoauth2->timecreated = $timenow;
933 $backpackoauth2->timemodified = $timenow;
934 $backpackoauth2->userid = $data['userid'];
935 $backpackoauth2->issuerid = 1;
936 $backpackoauth2->externalbackpackid = $data['externalbackpackid'];
937 $backpackoauth2->token = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
938 $backpackoauth2->refreshtoken = '0123456789abcdefghijk';
939 $backpackoauth2->expires = $timenow + 3600;
940 $backpackoauth2->scope = 'https://purl.imsglobal.org/spec/ob/v2p1/scope/assertion.create';
941 $backpackoauth2->scope .= ' https://purl.imsglobal.org/spec/ob/v2p1/scope/assertion.readonly offline_access';
942 $DB->insert_record('badge_backpack_oauth2', $backpackoauth2);
943
944 // Dummy badge_backpack data.
945 $backpack = new stdClass();
946 $backpack->userid = $data['userid'];
947 $backpack->email = 'student@behat.moodle';
948 $backpack->backpackuid = 0;
949 $backpack->autosync = 0;
950 $backpack->password = '';
951 $backpack->externalbackpackid = $data['externalbackpackid'];
952 $DB->insert_record('badge_backpack', $backpack);
953 }
285c7036 954}