apply-enrollment-rules.php ➔ getGroupMembers()   B
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 26
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 1
dl 0
loc 26
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
require_once('common.inc.php');
4
5
use Battis\BootstrapSmarty\NotificationMessage;
6
7
define('ENROLL', true);
8
// DELETE is the enrollment ID to be deleted (i.e. not a boolean value)
9
10
function getGroupMembers($group)
11
{
12
    global $customPrefs; // FIXME grown-ups don't code like this
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
13
    $members = array();
14
    $response = $customPrefs->query("
15
        SELECT *
16
            FROM `group-memberships`
17
            WHERE
18
                `group` = '$group'
19
    ");
20
    while ($membership = $response->fetch_assoc()) {
21
        $members[$membership['user']] = ENROLL;
22
    }
23
24
    $response = $customPrefs->query("
25
        SELECT *
26
            FROM `groups`
27
            WHERE
28
                `parent` = '$group';
29
    ");
30
    while ($child = $response->fetch_assoc()) {
31
        $members = array_replace($members, getGroupMembers($child['id']));
32
    }
33
34
    return $members;
35
}
36
37
define('STEP_INSTRUCTIONS', 1);
38
define('STEP_ENROLL', 2);
39
40
$step = (empty($_REQUEST['step']) ? STEP_INSTRUCTIONS : $_REQUEST['step']);
41
42
switch ($step) {
43
    case STEP_ENROLL:
44
        try {
45
            $rules = $customPrefs->query("
46
                SELECT *
47
                    FROM `enrollment-rules` as `rules`
48
                    LEFT JOIN
49
                        `groups` ON `groups`.`id` = `rules`.`group`
50
            ");
51
            $courses = array();
52
53
            /* walk through all the enrollment rules */
54
            while ($rule = $rules->fetch_assoc()) {
55
                /* find users whose role matches this rule... */
56
                if (!empty($rule['role'])) {
57
                    $response = $customPrefs->query("
58
                        SELECT *
59
                            FROM `users`
60
                            WHERE `role` = '{$rule['role']}'
61
                    ");
62
                    while ($row = $response->fetch_assoc()) {
63
                        $courses[$rule['course']][$row['id']] = ENROLL;
64
                    }
65
66
                /* ...or whose group matches this rule (recursively) */
67
                } elseif (!empty($rule['group'])) {
68
                    if (empty($courses[$rule['course']])) {
69
                        $courses[$rule['course']] = array();
70
                    }
71
                    $courses[$rule['course']] = array_replace(
72
                        $courses[$rule['course']],
73
                        getGroupMembers($rule['group'])
74
                    );
75
76
                /* ...or post an error, because rules expect a role or a group! */
77
                } else {
78
                    $toolbox->smarty_addMessage(
79
                        'Missing Role or Group',
80
                        "Rule ID {$rule['id']} is missing both a role and a group. It must have one or the other!",
81
                        NotificationMessage::ERROR
0 ignored issues
show
Deprecated Code introduced by
The constant Battis\BootstrapSmarty\NotificationMessage::ERROR has been deprecated with message: Use `DANGER` instead for consistency with Bootstrap

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
82
                    );
83
                }
84
            }
85
86
            // TODO worry about non-student default enrollment types
87
            /* process enrollments for each course */
88
            foreach ($courses as $courseId => $enrollees) {
89
                /* get the list of current enrollments for the course the rule refers to */
90
                $enrolled = 0;
91
                $deleted = 0;
92
                $current = 0;
93
                $courseExists = true;
94
                try {
95
                    $course = $toolbox->api_get("courses/$courseId");
96
                    $enrollments = $toolbox->api_get("courses/$courseId/enrollments");
97
                } catch (Exception $e) {
98
                    $courseExists = false;
99
                    $toolbox->smarty_addMessage(
100
                        "Course ID $courseId",
101
                        'This course does not exist (in this instance).',
102
                        NotificationMessage::WARNING
103
                    );
104
                }
105
106
                if ($courseExists) {
107
                    /* walk through the current enrollments and... */
108
                    $potentialDuplicates = array();
109
                    foreach ($enrollments as $enrollment) {
110
                        /* ignore observers -- they should all be assigned by API to track specific users */
111
                        if ($enrollment['type'] != 'ObserverEnrollment') {
112
                            /* ...clear users already enrolled... */
113
                            if (isset($enrollees[$enrollment['user']['id']])) {
114
                                unset($enrollees[$enrollment['user']['id']]);
115
116
                                /* make a note of this user in potential duplicates, in case they have
117
                                   multiple enrollments -- leave 'em all! */
118
                                $potentialDuplicates[$enrollment['user']['id']] = true;
119
                                $current++;
120
121
                            /* ...and purge users who should not be enrolled */
122
                            } elseif (!isset($potentialDuplicates[$enrollment['user']['id']])) {
123
                                if (empty($enrollment['id'])) {
124
                                    $toolbox->smarty_addMessage('No enrollment to delete', '<pre>'. print_r($enrollment, true) . '</pre>', NotificationMessage::WARNING);
125
                                } else {
126
                                    $toolbox->api_delete(
127
                                        "courses/$courseId/enrollments/{$enrollment['id']}",
128
                                        array(
129
                                            'task' => 'delete'
130
                                        )
131
                                    );
132
                                    $deleted++;
133
                                }
134
                            }
135
                        }
136
                    }
137
138
                    foreach ($enrollees as $userId => $status) {
139
                        try {
140
                            $toolbox->api_post(
141
                                "courses/$courseId/enrollments",
142
                                array(
143
                                    'enrollment[user_id]' => $userId,
144
                                    'enrollment[type]' => 'StudentEnrollment',
145
                                    'enrollment[enrollment_state]' => 'active',
146
                                    'enrollment[notify]' => 'false'
147
                                )
148
                            );
149
                            $enrolled++;
150
                        } catch (Exception $e) {
151
                            $toolbox->smarty_addMessage(
152
                                "User ID $userId",
153
                                "There was an error enrolling User ID $userId in <a target=\"_parent\" href=\"{$_SESSION[CANVAS_INSTANCE_URL]}/courses/$courseId/users\">{$course['name']}</a>. This user may have a mis-assigned role or no longer exist in this instance.",
154
                                NotificationMessage::ERROR
0 ignored issues
show
Deprecated Code introduced by
The constant Battis\BootstrapSmarty\NotificationMessage::ERROR has been deprecated with message: Use `DANGER` instead for consistency with Bootstrap

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
155
                            );
156
                        }
157
                    }
158
                    $toolbox->smarty_addMessage(
159
                        $course['name'],
160
                        "After applying enrollment rules, <a target=\"_parent\" href=\"{$_SESSION[CANVAS_INSTANCE_URL]}/courses/$courseId/users\">$enrolled new users were enrolled in this course</a>, $current users were unchanged, and $deleted users were removed."
161
                    );
162
                }
163
            }
164
        } catch (Exception $e) {
165
            $toolbox->exceptionErrorMessage($e);
166
        }
167
168
        /* flows into STEP_INSTRUCTIONS */
169
170
    case STEP_INSTRUCTIONS:
171
        $toolbox->smarty_assign('formHidden', array('step' => STEP_ENROLL));
172
        $toolbox->smarty_display(basename(__FILE__, '.php') . '/instructions.tpl');
173
}
174