-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlocallib.php
1644 lines (1400 loc) · 60.9 KB
/
locallib.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The plugin-specific library of functions. Used for testing too.
*
* @package enrol_selma
* @copyright 2020 LearningWorks <selma@learningworks.ac.nz>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
use core_course\customfield\course_handler;
use core_customfield\api;
use enrol_selma\local\course;
use enrol_selma\local\factory\property_map_factory;
use enrol_selma\local\factory\entity_factory;
use enrol_selma\local\student;
use enrol_selma\local\teacher;
use enrol_selma\local\user;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/lib.php');
require_once(dirname(__FILE__, 3) . '/admin/tool/uploaduser/locallib.php');
require_once(dirname(__FILE__, 3) . '/user/lib.php');
require_once(dirname(__FILE__, 3) . '/group/lib.php');
/**
* Function to add this intake to that course.
*
* @param int $intakeid ID of intake to add to course.
* @param int $courseid ID of course the intake should be added to.
* @param array $customfields Intake custom fields to add the the group customtext.
* @return array Array of success status & bool of true if success, along with message.
* @throws coding_exception
* @throws dml_exception|moodle_exception
*/
function enrol_selma_add_intake_to_course(int $intakeid, int $courseid, array $customfields) {
global $DB, $USER;
// Status tracker.
$notok = false;
// Set status to 'we don't know what went wrong'. We will set this to potential known causes further down.
$status = get_string('status_other', 'enrol_selma');
// Var 'added' of false means something didn't work. Changed if successfully added user to intake.
$added = false;
// Give more detailed response message to user.
$message = get_string('status_other_message', 'enrol_selma');
// TODO - Add intake to course.
// Check if course exists.
$courseexists = $DB->record_exists('course', array('id' => $courseid));
if ($courseexists) {
// Check if intake exists.
$intakeexists = $DB->record_exists('enrol_selma_intake', array('id' => $intakeid));
if ($intakeexists) {
// Add enrol_selma instance to course, if none.
// The course really should exist, as we check the DB for the ID above.
$course = get_course($courseid);
$enrolinstance = (new enrol_selma_plugin())->add_instance($course);
// TODO - Do I even need to return that we created a instance/group or not, or do we just do it and return success/fail?
// Could not add enrol_selma instance.
if (is_null($enrolinstance)) {
// Set status to 'forbidden'.
$status = get_string('status_almostok', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_almostok_message', 'enrol_selma') .
get_string('forbidden_instance_add', 'enrol_selma', $courseid);
// Status demoted to 'almost ok'.
$notok = true;
// We can continue - no 'return', as the instance already exists.
}
// If we successfully added enrolinstance.
// Create group in course, if needed.
$groupfound = groups_get_group_by_idnumber($courseid, $intakeid);
$groupid = 0;
if ($groupfound === false) {
// Group not found, add one.
// The intake really should exist, as we check the DB for the ID above.
$intake = $DB->get_record('enrol_selma_intake', array('id' => $intakeid));
$group = new stdClass();
$group->name = $intake->name;
$group->courseid = $courseid;
$group->idnumber = $intakeid;
// Add the custom fields if they are present.
foreach ($customfields as $key => $value) {
$group->{$key} = $value;
}
// Create group.
$newgroup = groups_create_group($group);
// Set status if we could not create group for some reason.
if (!isset($newgroup) || $newgroup === false) {
// Set status to 'forbidden'.
$status = get_string('status_almostok', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_almostok_message', 'enrol_selma') .
get_string('forbidden_group_add', 'enrol_selma', array('intake' => $intakeid, 'course' => $courseid));
// Status demoted to 'almost ok'.
$notok = true;
// We can continue - no 'return', as the process can technically continue without a group.
} else {
// Group created.
$groupid = $newgroup;
}
} else {
// Else, group exists already.
$groupid = $groupfound->id;
foreach ($customfields as $key => $value) {
if (empty($groupfound->{$key})) {
// Not even set customfield to be set.
$groupfound->{$key} = $value;
} else if ($groupfound->{$key} !== $value) {
// A changed custom field set.
$groupfound->{$key} = $value;
}
}
try {
groups_update_group($groupfound);
} catch (\moodle_exception | \Exception $exception) {
// Ignore these so that the sync can carry on.
// Todo: these issues should be logged somewhere.
}
}
// Build relationship - group, course, enrol instance, intake.
// Create object to record relation between courses, intakes & groups.
$relate = new stdClass();
$relate->courseid = $courseid;
$relate->intakeid = $intakeid;
$relate->groupid = $groupid;
$relate->usermodified = $USER->id;
$exists = $DB->record_exists('enrol_selma_course_intake',
array('courseid' => $relate->courseid, 'intakeid' => $relate->intakeid)
);
if ($exists) {
// Set status to 'nothing new here'.
$status = get_string('status_nonew', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_nonew_message', 'enrol_selma');
$notok = true;
} else {
// Store relation to DB.
$added = $DB->insert_record('enrol_selma_course_intake', $relate, false);
}
// If everything's gone perfectly so far, set the status as such.
if ($added && !$notok) {
// Set status to 'OK'.
$status = get_string('status_ok', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_ok_message', 'enrol_selma');
}
// If the intake was not added to the course, we have ultimately failed...
if (!$added && !$notok) {
// Set status to 'fail'.
$status = get_string('status_internalfail', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_internalfail_message', 'enrol_selma');
}
// Enrol users to course - use core functions, if possible. TODO - Queue?
// Use scheduled task?
// Returned details - success hopefully!
return ['status' => $status, 'added' => $added, 'message' => $message];
}
// Set status to 'not found'.
$status = get_string('status_notfound', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_notfound_message', 'enrol_selma') .
get_string('status_notfound_detailed_message', 'enrol_selma',
get_string('add_intake_to_course_parameters::intakeid', 'enrol_selma'));
// Returned details - failed...
return ['status' => $status, 'added' => $added, 'message' => $message];
}
// Set status to 'not found'.
$status = get_string('status_notfound', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_notfound_message', 'enrol_selma') .
get_string('status_notfound_detailed_message', 'enrol_selma', get_string('course'));
// Returned details - failed...
return ['status' => $status, 'added' => $added, 'message' => $message];
}
/**
* The function to add the specified user to an intake.
*
* @param string $studentid SELMA ID of student to add to intake.
* @param int $intakeid SELMA intake ID the user should be added to.
* @return array Array of success status & bool if successful/not, message.
* @throws coding_exception|dml_exception
*/
function enrol_selma_add_student_to_intake(string $studentid, int $intakeid) {
global $DB;
// Track any warning messages.
$warnings = [];
// Get real Moodle user ID.
$muser = $DB->get_record('user', array('idnumber' => $studentid), 'id');
// If user doesn't exist yet (or they have not been 'linked' to SELMA yet).
if (!$muser) {
$warnings[] = [
'item' => get_string('pluginname', 'enrol_selma'),
'itemid' => 1,
'warningcode' => get_string('warning_code_notfound', 'enrol_selma'),
'message' => get_string('warning_message_notfound', 'enrol_selma', $studentid)
];
// Return 'not found' status.
return ['warnings' => $warnings];
}
return enrol_selma_add_user_to_intake($muser->id, $intakeid, $studentid);
}
/**
* The function to add the specified user to an intake.
*
* @param string $teacherid SELMA ID of teacher to add to intake.
* @param int $intakeid SELMA intake ID the user should be added to.
* @return array Array of success status & bool if successful/not, message.
* @throws coding_exception|dml_exception
*/
function enrol_selma_add_teacher_to_intake(string $teacherid, int $intakeid) {
// Track any warning messages.
$warnings = [];
// Get real Moodle user ID.
$muser = enrol_selma_get_teacher($teacherid);
// If user doesn't exist yet (or they have not been 'linked' to SELMA yet).
if (!$muser) {
$warnings[] = [
'item' => get_string('pluginname', 'enrol_selma'),
'itemid' => 1,
'warningcode' => get_string('warning_code_notfound', 'enrol_selma'),
'message' => get_string('warning_message_notfound', 'enrol_selma', $teacherid)
];
// Return 'not found' status.
return ['warnings' => $warnings];
}
return enrol_selma_add_user_to_intake($muser['id'], $intakeid, $teacherid, 'teacher');
}
/**
* The function to add the specified user to an intake.
*
* @param int $muserid Moodle user ID - to add to intake.
* @param int $intakeid SELMA intake ID the user should be added to.
* @param int $selmaid SELMA user ID being added to intake.
* @param string $type Type of user - teacher or student.
* @return array Array of success status & bool if successful/not, message.
* @throws coding_exception|dml_exception
*/
function enrol_selma_add_user_to_intake(int $muserid, int $intakeid, int $selmaid, string $type = 'student') {
// Var 'added' of false means something didn't work. Changed further down if successfully added user to intake.
$added = false;
// Track any warning messages.
$warnings = [];
// TODO - also eventually check if we need to enrol user into anything once we have all the necessary functions.
// If added successfully, return success message.
if (enrol_selma_relate_user_to_intake($muserid, $intakeid, $type)) {
// User added to intake.
$added = true;
}
$enrolled = false;
// If user has been added, we need to enrol them to the intake's associated course(s).
if ($added) {
$enrolled = enrol_selma_enrol_user($muserid, $intakeid, $type);
}
if ($enrolled !== false) {
// TODO - Send back warnings from enrol attempt(s)?
return ['courses' => $enrolled];
}
$warnings[] = [
'item' => get_string('pluginname', 'enrol_selma'),
'itemid' => 1,
'warningcode' => get_string('warning_code_unknown', 'enrol_selma'),
'message' => get_string('warning_message_unknown', 'enrol_selma', $selmaid)
];
// Returned details - failed (probably)...
return ['warnings' => $warnings];
}
/**
* Creates the course based on details provided.
*
* @param array $course Array of course details to create course.
* @return array Array containing the status of the request, created course's ID, and appropriate message.
*/
function enrol_selma_create_course(array $course) {
global $CFG;
// Set status to 'we don't know what went wrong'. We will set this to potential known causes further down.
$status = get_string('status_other', 'enrol_selma');
// Courseid of null means something didn't work. Changed if successfully created a course.
$courseid = null;
// Set to give more detailed response message to user.
$message = get_string('status_other_message', 'enrol_selma');
// Check and validate everything that's needed (as minimum) by this function.
$warnings = [];
$context = context_system::instance();
// Check if user has permission to create a course.
require_capability('moodle/course:create', $context);
// Check if we have a place to put the course.
if (get_config('enrol_selma', 'newcoursecat') === false) {
throw new moodle_exception('error_noconfig',
'enrol_selma',
$CFG->wwwroot . '/admin/settings.php?section=usersettingsselma',
'newcoursecat'
);
}
// Check if config(s) we use later have been set. These are optional, so just warn.
if (get_config('enrol_selma', 'selmacoursetags') === false) {
$warnings[] = [
'item' => get_string('pluginname', 'enrol_selma'),
'itemid' => 1,
'warningcode' => get_string('warning_code_noconfig', 'enrol_selma'),
'message' => get_string('warning_message_noconfig', 'enrol_selma', 'selmacoursetags')
];
// Not essential, so can continue - but warn.
}
// Prep tags - find & replace text and convert to array.
$tags = get_config('enrol_selma', 'selmacoursetags');
if ($tags !== false) {
$tags = str_replace(['{{fullname}}', '{{shortname}}'], [$course['fullname'], $course['shortname']], $tags);
$tags = explode(',', $tags);
} else {
$tags = '';
}
// Construct course object.
$coursedata = new stdClass();
// TODO - what if the setting has not been configured yet, but we get a call to create_course or if it's been deleted?
// The default category to put the course in.
$coursedata->category = get_config('enrol_selma', 'newcoursecat');
// Generated? Remember - visible to users.
$coursedata->fullname = $course['fullname'];
// Generated? Remember - visible to users.
$coursedata->shortname = $course['shortname'];
// Generated?
$coursedata->idnumber = $course['idnumber'];
// Optional - based on category if not set.
$coursedata->visible = get_config('moodlecourse', 'visible');
// Add the user specified in 'selmacoursetags' setting.
$coursedata->tags = $tags;
// Consider course_updated() in lib.php? Check out lib/enrollib.php:409.
$coursecreated = create_course($coursedata);
// Check out course/externallib.php:831.
// TODO - Add enrol_selma to course. Is this enough? What to do if false is returned?
// Instantiate & add SELMA enrolment instance to course.
(new enrol_selma_plugin)->add_instance($coursecreated);
// TODO - proper check/message?
// Check if course created successfully.
if (isset($coursecreated->id) && $coursecreated->id > 1) {
$status = get_string('status_ok', 'enrol_selma');
$courseid = $coursecreated->id;
$message = get_string('status_ok_message', 'enrol_selma');
// Returned details - success!
if (empty($warnings)) {
return ['status' => $status, 'courseid' => $courseid, 'message' => $message];
} else {
return ['status' => $status, 'courseid' => $courseid, 'message' => $message, 'warnings' => $warnings];
}
}
$status = get_string('status_internalfail', 'enrol_selma');
$message = get_string('status_internalfail_message', 'enrol_selma');
// Returned details - failed...
if (empty($warnings)) {
return ['status' => $status, 'courseid' => $courseid, 'message' => $message];
} else {
return ['status' => $status, 'courseid' => $courseid, 'message' => $message, 'warnings' => $warnings];
}
}
/**
* Creates the intake record based on details provided.
*
* @param array $intake Array of intake details, used to create intake.
* @return array Array containing the status of the request, the created intake's ID, and appropriate message.
*/
function enrol_selma_create_intake(array $intake) {
global $USER, $DB;
// Set status to 'we don't know what went wrong'. We will set this to potential known causes further down.
$status = get_string('status_other', 'enrol_selma');
// Intakeid of null means something didn't work. Changed if successfully created the intake record.
$intakeid = null;
// Set to give more detailed response message to user.
$message = get_string('status_other_message', 'enrol_selma');
// TODO - Any additional checks - as we're inserting to DB?
// TODO - Handle date values, time seems to be set to current time.
$intake['intakestartdate'] = DateTime::createFromFormat('Y-m-d', $intake['intakestartdate']);
$intake['intakeenddate'] = DateTime::createFromFormat('Y-m-d', $intake['intakeenddate']);
// Build record.
$data = new stdClass();
$data->id = $intake['intakeid'];
$data->programmeid = $intake['programmeid'];
$data->code = $intake['intakecode'];
$data->name = $intake['intakename'];
$data->startdate = $intake['intakestartdate']->getTimestamp();
$data->enddate = $intake['intakeenddate']->getTimestamp();
$data->usermodified = $USER->id;
$data->timecreated = time();
$data->timemodified = time();
// Check if record exists before inserting.
if (!$DB->record_exists('enrol_selma_intake', array('id' => $data->id))) {
// TODO - use raw insert? No safety checks.
// Try to insert to DB, Moodle will throw exception, if necessary.
$DB->insert_record_raw('enrol_selma_intake', $data, null, null, true);
// Set status to 'OK'.
$status = get_string('status_ok', 'enrol_selma');
// Set intakeid to the one we just created.
$intakeid = $data->id;
// Use to give more detailed response message to user.
$message = get_string('status_ok_message', 'enrol_selma');
} else {
// Record could not be created - probably because it already exists.
// Set status to 'Already Reported'.
$status = get_string('status_nonew', 'enrol_selma');
// Give more detailed response message to user.
$message = get_string('status_nonew_message', 'enrol_selma');
}
// Returned details - failed if not changed above...
return ['status' => $status, 'intakeid' => $intakeid, 'message' => $message];
}
/**
* Creates users with details provided.
*
* @param array|null $users Array of users' details required to create an account for them.
* @return array Array containing the status of the request, userid of users created, and appropriate message.
*/
function enrol_selma_create_users(array $users) {
$existinguser = [];
// Set status to 'we don't know what went wrong'. We will set this to potential known causes further down.
$status = get_string('status_other', 'enrol_selma');
// If $users = null, then it means we didn't find anything/something went wrong. Changed if successfully created a user(s).
$userids = [];
// Set to give more detailed response message to user.
$message = get_string('status_other_message', 'enrol_selma');
// For each user received, process...
foreach ($users as $user) {
// If no username set, set to firstname.lastname format.
if (!isset($user['username']) || empty($user['username'])) {
$user['username'] = strtolower($user['forename'] . '.' . $user['lastname']);
}
$createduser = enrol_selma_user_from_selma_data($user);
$createduser->save();
// Add to list of created userids to be returned.
$userids[] = $createduser->id;
}
// Check if existing users were found & update status/message.
if (isset($existinguser) && !empty($existinguser)) {
$status = get_string('status_almostok', 'enrol_selma');
$message = get_string('status_almostok_message', 'enrol_selma') .
' ' .
get_string('status_almostok_existing_message', 'enrol_selma', implode(', ', $existinguser));
// Above message okay for if we managed to create some users alongside some duplicates. Below is if only duplicates found,
// But no new accounts created.
if (empty($userids) || !isset($userids)) {
$status = get_string('status_nonew', 'enrol_selma');
$message = get_string('status_nonew_message', 'enrol_selma');
}
} else {
// If we have no duplicates & created some users - best type of success.
if (isset($userids) && !empty($userids)) {
$status = get_string('status_ok', 'enrol_selma');
$message = get_string('status_ok_message', 'enrol_selma');
} else {
// No duplicates & no users created - fail/nothing done.
$status = get_string('status_nocontent', 'enrol_selma');
$message = get_string('status_nocontent_message', 'enrol_selma');
}
}
// Returned details - failed...
return ['status' => $status, 'userids' => $userids, 'message' => $message];
}
/**
* Get all the courses that's not in any excluded category - excludecoursecat setting.
*
* @param int $amount Number of records to retrieve - get all by default.
* @param int $page Which 'page' to retrieve from the DB - works in conjunction with $amount.
* @return array Array containing the status of the request, courses found, and appropriate message.
* @throws moodle_exception Exception thrown when invalid/negative params are given.
*/
function enrol_selma_get_all_courses(int $amount = 0, int $page = 1) {
global $DB;
// TODO - $amount & $page needs to be positive values.
if ($amount < 0 || $page < 0) {
throw new moodle_exception('exception_bepositive', 'enrol_selma');
}
// To keep track of which DB 'page' to look on.
$dbpage = $page;
$nextpage = -1;
// Set status to 'we don't know what went wrong'. We will set this to potential known causes further down.
$status = get_string('status_other', 'enrol_selma');
// If courses = null, then it means we didn't find anything/something went wrong. Changed if successfully found a course(s).
$courses = null;
// Set to give more detailed response message to user.
$message = get_string('status_other_message', 'enrol_selma');
// Used to calculate the right place to start from. Page index starts at 0.
if ($page > 0) {
$dbpage = $page - 1;
}
// Vars to keep track of which page/amount of courses to get.
$limitfrom = $amount * $dbpage;
$limitnum = $amount;
// If amount is zero, we get all the courses.
if ($amount === 0) {
$limitfrom = 0;
}
// Let's check if any categories need to be excluded.
$excats = get_config('enrol_selma', 'excludecoursecat');
if (isset($excats) && !empty($excats)) {
// Found category to exclude.
$excats = explode(',', $excats);
}
// Exclude 'site' course category.
$excats[] = '0';
// Create SQL to exclude 'excluded' categories.
list($sqlfragment, $params) = $DB->get_in_or_equal($excats, SQL_PARAMS_NAMED, null, false);
// Get those courses.
$courses = $DB->get_records_select(
'course',
"category $sqlfragment",
$params,
null,
'id,fullname,shortname,idnumber',
$limitfrom,
$limitnum
);
// Check if we found anything.
if (empty($courses) || !isset($courses)) {
// No courses found, update status/message.
$status = get_string('status_notfound', 'enrol_selma');
$message = get_string('status_notfound_message', 'enrol_selma');
// Return status.
return ['status' => $status, 'courses' => $courses, 'nextpage' => $nextpage, 'message' => $message];
}
// The next page the requester should request.
if ($amount !== 0 && count($courses) == $amount) {
$nextpage = $page + 1;
}
// Courses retrieved successfully, set statusses, messages, vars appropriately.
$status = get_string('status_ok', 'enrol_selma');
// Var $courses already set.
$message = get_string('status_ok_message', 'enrol_selma');
// Returned details.
return ['status' => $status, 'courses' => $courses, 'nextpage' => $nextpage, 'message' => $message];
}
/**
* Validates and checks if profilemapping is in order.
*
* @return array Returns array of the duplicated values used for profile field mapping.
*/
function enrol_selma_validate_profile_mapping() {
// TODO - Get all and filter dupes or be specific?
// TODO - Also check the types match. e.g. NSN -> Integer.
// Get all the plugin's configs.
$selmasettings = (array) get_config('enrol_selma');
// Check each setting if profilemap.
foreach ($selmasettings as $key => $value) {
// We only check if profilemaps have duplicates.
if (stripos($key, 'upm_') === false || empty($value)) {
// Not profilemap - remove.
unset($selmasettings[$key]);
}
}
// Count how many times each value shows up.
$duplicatesfound = array_count_values($selmasettings);
// Remove if it's only turned up once, and then return array's keys as values instead.
$duplicatesfound = array_keys(array_diff($duplicatesfound, [1]));
// Return all duplicates found, if any.
return $duplicatesfound;
}
/**
* Finds which SELMA student fields are mapped to which Moodle profile fields.
*
* @return array Returns array of which Moodle fields the SELMA fields are mapped to.
*/
function enrol_selma_get_profile_mapping() {
$searchstring = 'upm_';
// TODO - Get all and filter dupes or be specific?
// Get all the plugin's configs.
$profilemap = (array) get_config('enrol_selma');
// Check each setting if profilemap.
foreach ($profilemap as $key => $value) {
// Check if a profilemapping config.
if (stripos($key, $searchstring) === false) {
// Not profilemap - remove.
unset($profilemap[$key]);
}
}
// Remove prefix from fields.
$profilemap = enrol_selma_remove_arrkey_substr($profilemap, $searchstring);
// Return all profilemaps found, if any.
return $profilemap;
}
/**
* Loops through an array's keys and removes any occurrence of the given substring.
*
* @param array $checkarray The array to search through.
* @param string $substring The substring to search for.
* @return array Returns array with the substring removed from its keys.
*/
function enrol_selma_remove_arrkey_substr(array $checkarray, string $substring) {
// Note - can't use array_walk as we'll be updating the array structure (not only it's values).
// See https://www.php.net/manual/en/function.array-walk.php#refsect1-function.array-walk-parameters.
// Loop through the array to manually update the keys.
foreach ($checkarray as $key => $value) {
// Check if the key contains the substring.
if (stripos($key, $substring) !== false) {
// Found a match! Add an entry to the array with the updated key and same value, then remove the old entry.
$newkey = str_replace($substring, '', $key);
$checkarray[$newkey] = $value;
unset($checkarray[$key]);
}
}
// Return array with updated keys, if any.
return $checkarray;
}
/**
* Loops through an array's keys and prepends any occurrence of the given substring.
*
* @param array $checkarray The array to search through.
* @param string $substring The substring to search for.
* @return array Returns array with the substring prepended to its keys.
*/
function enrol_selma_prepend_arrkey_substr(array $checkarray, string $substring) {
// Note - can't use array_walk as we'll be updating the array structure (not only it's values).
// See https://www.php.net/manual/en/function.array-walk.php#refsect1-function.array-walk-parameters.
// Loop through the array to manually update the keys.
foreach ($checkarray as $key => $value) {
// Prepend substring to each key.
$newkey = $substring . $key;
// Set new key.
$checkarray[$newkey] = $value;
// Unset old key.
unset($checkarray[$key]);
}
// Return array with updated keys, if any.
return $checkarray;
}
/**
* Array of user profile fields we don't want to write to - for data integrity and security.
*
* @return array $keys Returns array of blacklisted user fields.
*/
function enrol_selma_get_blacklisted_user_fields() {
$keys = [
'id',
'auth',
'confirmed',
'policyagreed',
'deleted',
'suspended',
'mnethostid',
'password',
'emailstop',
'icq',
'skype',
'yahoo',
'aim',
'msn',
'lang',
'calendartype',
'theme',
'timezone',
'firstaccess',
'lastaccess',
'lastlogin',
'currentlogin',
'lastip',
'secret',
'picture',
'url',
'imagealt',
'lastnamephonetic',
'firstnamephonetic',
'moodlenetprofile',
'descriptionformat',
'mailformat',
'maildigest',
'maildisplay',
'autosubscribe',
'trackforums',
'timecreated',
'timemodified',
'trustbitmask'
];
return $keys;
}
/**
* Array of all custom user profile fields on the site.
*
* @return array $customoptions Returns array of custom profile fields.
*/
function enrol_selma_get_custom_profile_fields() {
global $DB;
$customoptions = [];
// Get custom fields.
$customfields = $DB->get_records('user_info_field', [], null, 'shortname');
// If we found customprofile fields, we need to include those.
if (!empty($customfields) && isset($customfields)) {
// Prepend with 'profile_field_' to make identifiable as custom user field.
$customoptions = preg_filter('/^/', 'profile_field_', array_keys($customfields));
}
return $customoptions;
}
/**
* Load all custom profile fields on the site into user object as properties.
*
* @param user $user User object to load fields into.
* @return user $user Returns array of custom profile fields.
*/
function enrol_selma_load_custom_profile_fields(user $user) {
$allcustomfields = enrol_selma_get_custom_profile_fields();
foreach ($allcustomfields as $field) {
// TODO - set to string by default for now - add checks for type.
$user->$field = '';
}
return $user;
}
/**
* Array of all allowed user profile fields - including custom fields and excluding blacklisted fields.
*
* @return array $keys Returns array of all allowed user fields.
*/
function enrol_selma_get_allowed_user_fields() {
// Get core fields.
$alloptions = get_user_fieldnames();
// List of user profile fields we don't want to write to - for data integrity and security.
$blacklistkeys = enrol_selma_get_blacklisted_user_fields();
// Get custom fields.
$customoptions = enrol_selma_get_custom_profile_fields();
$alloptions = array_merge($alloptions, $customoptions);
// TODO - Need to re-create index with array_combine() - it sets each key to its value, to get shortname easier?
// Remove any blacklisted profile fields from the list of options.
$allowed = array_values(array_diff($alloptions, $blacklistkeys));
$allowed = array_combine($allowed, $allowed);
return $allowed;
}
/**
* Loads user based on given (Moodle) ID.
*
* @param int $id User's Moodle ID value.
*/
function enrol_selma_user_from_id(int $id) {
global $DB;
$user = new user();
// Set id, as it's on the blacklisted fields - we don't want the user to set the user's id.
$user->id = $id;
// Should only be one, so we use get_record. Also, only the allowed fields.
$dbuser = (array) $DB->get_record('user', array('id' => $user->id));
// Set core fields/properties.
$user->set_properties($dbuser);
// Get custom profile fields.
$customfields = enrol_selma_get_user_custom_field_data($user->id);
// Set custom profile fields.
if (isset($customfields)) {
$customfields = enrol_selma_prepend_arrkey_substr($customfields, 'profile_field_');
$user->set_properties($customfields);
}
return $user;
}
/**
* Update the user's properties with the SELMA data.
*
* @param array $selmauser SELMA user data to be transcribed to Moodle user data.
*/
function enrol_selma_user_from_selma_data($selmauser) {
$user = new user();
// Use profile field mapping to capture user data.
$profilemapping = enrol_selma_get_profile_mapping();
// Assign each SELMA user profile field to the Moodle equivalent.
foreach ($selmauser as $field => $value) {
// Translate to Moodle field.
$element = $profilemapping[$field];
// Set field to value.
$user->set_property($element, $value);
}
return $user;
}
/**
* Creates record in DB of relationship between user & intake.
*
* @param int $userid User we're adding to an intake.
* @param int $intakeid Intake ID user should be added to.
* @param string $type Type of user - student or teacher.
* @return bool $inserted Bool indicating success/failure of inserting record to DB.
* @throws dml_exception
*/
function enrol_selma_relate_user_to_intake(int $userid, int $intakeid, string $type = 'student') {
global $DB, $USER;
// Todo - Should we be able to add users to an intake before the intake exists in Moodle (pre-create)?
// Check if intake exists.
if ($DB->record_exists('enrol_selma_intake', array('id' => $intakeid)) === false) {
return false;
}
// Construct data object for DB.
$data = new stdClass();
$data->userid = $userid;
$data->intakeid = $intakeid;
$data->usermodified = $USER->id;
$data->timecreated = time();
$data->timemodified = time();
// Add to student or teacher table.
$table = 'enrol_selma_student_intake';
if ($type === 'teacher') {
$table = 'enrol_selma_teacher_intake';
}
// Prevent double up entries.
if ($DB->record_exists($table, ['userid' => $userid, 'intakeid' => $intakeid])) {
return true;
} else {
return $DB->insert_record($table, $data, false);
}
}
/**
* Get all of a user's custom profile field data.
*
* @param int $id User's Moodle ID.
* @return array $customfields Associative array with customfield's shortname as key and user's data as value.
* @throws dml_exception
*/
function enrol_selma_get_user_custom_field_data(int $id) {
global $DB;
// Keep track of given user's data.
$userdata = [];
// Get the fields and data for the user.
$customfields = profile_get_custom_fields();
$fielddata = $DB->get_records('user_info_data', array('userid' => $id), null, 'id, fieldid, data');
// Map the user's data to the corresponding customfield shortname.
foreach ($fielddata as $data) {
$userdata[$customfields[$data->fieldid]->shortname] = $data->data;
}
return $userdata;
}
/**
* Checks if a user is associated to an intake.
*
* @param int $userid User we're checking.
* @param int $intakeid Intake ID user should be checked against.
* @param string $type Type of user - student or teacher.
* @return bool Bool indicating if user is in intake.
* @throws dml_exception
*/
function enrol_selma_user_is_in_intake(int $userid, int $intakeid, string $type = 'student') {
global $DB;
$table = 'enrol_selma_student_intake';
if ($type === 'teacher') {
$table = 'enrol_selma_teacher_intake';
}
return $DB->record_exists($table, array('userid' => $userid, 'intakeid' => $intakeid));