public function create_groups_for_new_courses()

in local/o365/classes/feature/usergroups/coursegroups.php [92:270]


    public function create_groups_for_new_courses() {
        global $DB;

        $createteams = get_config('local_o365', 'createteams');
        if ($createteams === 'onall' || $createteams === 'oncustom') {
            $coursesenabled = \local_o365\feature\usergroups\utils::get_enabled_courses();
            if (empty($coursesenabled)) {
                $this->mtrace('Custom group creation is enabled, but no courses are enabled.');
                return false;
            }
        } else {
            $this->mtrace('Group creation is disabled.');
            return false;
        }

        if (is_array($coursesenabled)) {
            [$coursesinsql, $coursesparams] = $DB->get_in_or_equal($coursesenabled);
        } else {
            $coursesinsql = '';
            $coursesparams = [];
        }

        // First process courses with groups that have been "soft-deleted".
        $sql = 'SELECT crs.id as courseid,
                       obj.*
                  FROM {course} crs
                  JOIN {local_o365_objects} obj ON obj.type = ? AND obj.subtype = ? AND obj.moodleid = crs.id';
        $params = ['group', 'course'];
        if (!empty($coursesinsql)) {
            $sql .= ' WHERE crs.id '.$coursesinsql;
            $params = array_merge($params, $coursesparams);
        }
        $objectrecs = $DB->get_recordset_sql($sql, $params);
        foreach ($objectrecs as $objectrec) {
            $metadata = (!empty($objectrec->metadata)) ? @json_decode($objectrec->metadata, true) : [];
            if (is_array($metadata) && !empty($metadata['softdelete'])) {
                $this->mtrace('Attempting to restore group for course #'.$objectrec->courseid);
                $result = $this->restore_group($objectrec->id, $objectrec->objectid, $metadata);
                if ($result === true) {
                    $this->mtrace('....success!');
                } else {
                    $this->mtrace('....failed. Group may have been deleted for too long.');
                }
            }
        }

        // Process courses without an associated group.
        $sql = 'SELECT crs.*
                  FROM {course} crs
             LEFT JOIN {local_o365_objects} obj ON obj.type = ? AND obj.subtype = ? AND obj.moodleid = crs.id
                 WHERE obj.id IS NULL AND crs.id != ? AND crs.visible != 0';
        // The "crs.visible != 0" is used to filter out courses in the process of copy or restore, which may contain incorrect or
        // incomplete contents.
        $params = ['group', 'course', SITEID];
        if (!empty($coursesinsql)) {
            $sql .= ' AND crs.id '.$coursesinsql;
            $params = array_merge($params, $coursesparams);
        }
        $courselimit = get_config('local_o365', 'courses_per_task');
        if (!$courselimit) {
            $courselimit = 5;
        }
        $courses = $DB->get_recordset_sql($sql, $params, 0, $courselimit);
        $coursesprocessed = 0;
        foreach ($courses as $course) {
            $coursesprocessed++;
            $createclassteam = false;
            $creategrouponly = true;
            $ownerid = null;
            $teacherid = null;

            if (\local_o365\feature\usergroups\utils::course_is_group_feature_enabled($course->id, 'team')) {
                $creategrouponly = false;
                $teacherids = static::get_team_owner_ids_by_course_id($course->id);
                foreach ($teacherids as $teacherid) {
                    if ($ownerid = $DB->get_field('local_o365_objects', 'objectid', ['type' => 'user', 'moodleid' => $teacherid])) {
                        $createclassteam = true;
                        break;
                    }
                }
            }

            if ($createclassteam) {
                // Create class team directly.
                try {
                    $objectrec = $this->create_class_team($course, $ownerid);
                } catch (\Exception $e) {
                    $this->mtrace('Could not create class team for course #' . $course->id . '. Reason: ' . $e->getMessage());
                    continue;
                }
            } else if ($creategrouponly || get_config('local_o365', 'group_creation_fallback') == true) {
                // Create group.
                try {
                    $objectrec = $this->create_group($course);
                } catch (\Exception $e) {
                    $this->mtrace('Could not create group for course #' . $course->id . '. Reason: ' . $e->getMessage());
                    continue;
                }
            } else {
                // Option to fall back to group is disabled, and Team owner is not found.
                $this->mtrace('Skip creating class team for course #' . $course->id . '. Reason: missing Team owner');
                continue;
            }

            $retrycounter = 0;
            while ($retrycounter <= API_CALL_RETRY_LIMIT) {
                if ($retrycounter) {
                    $this->mtrace('..... Retry #' . $retrycounter);
                    sleep(10);
                }
                try {
                    $this->resync_group_membership($course->id, $objectrec['objectid']);
                    break;
                } catch (\Exception $e) {
                    $this->mtrace('Could not sync users to group for course #'.$course->id.'. Reason: '.$e->getMessage());
                    $retrycounter++;
                }
            }
        }
        if (empty($coursesprocessed)) {
            $this->mtrace('All courses have a group recorded.');
        } else {
            $this->mtrace('Processed courses: '.$coursesprocessed);
        }
        $courses->close();

        // Process team sync changes.
        $sql = 'SELECT crs.*
                  FROM {course} crs
             LEFT JOIN {local_o365_objects} obj_g ON obj_g.type = ? AND obj_g.subtype = ? AND obj_g.moodleid = crs.id
             LEFT JOIN {local_o365_objects} obj_t ON obj_t.type = ? AND obj_t.subtype = ? AND obj_t.moodleid = crs.id
                 WHERE obj_g.id IS NOT NULL
                   AND obj_t.id IS NULL
                   AND crs.id != ?';
        $params = ['group', 'course', 'group', 'courseteam', SITEID];
        if (!empty($coursesinsql)) {
            $sql .= ' AND crs.id ' . $coursesinsql;
            $params = array_merge($params, $coursesparams);
        }
        $courses = $DB->get_recordset_sql($sql, $params);
        $coursesprocessed = 0;

        // Get app ID.
        if (!empty($courses)) {
            $appid = get_config('local_o365', 'moodle_app_id');
        }

        foreach ($courses as $course) {
            if (\local_o365\feature\usergroups\utils::course_is_group_feature_enabled($course->id, 'team')) {
                $this->mtrace('Attempting to create team for course #' . $course->id . '...');
                $coursesprocessed++;
                $groupobjectrec = $DB->get_record('local_o365_objects',
                    ['type' => 'group', 'subtype' => 'course', 'moodleid' => $course->id]);
                if (empty($groupobjectrec)) {
                    $errmsg = 'Could not find group object ID in local_o365_objects for course ' . $course->id;
                    $errmsg .= 'Please ensure group exists first.';
                    $this->mtrace($errmsg);
                    continue;
                }
                $teacherids = static::get_team_owner_ids_by_course_id($course->id);
                $hasowner = false;
                foreach ($teacherids as $teacherid) {
                    if ($ownerid = $DB->get_field('local_o365_objects', 'objectid', ['type' => 'user', 'moodleid' => $teacherid])) {
                        $hasowner = true;
                        break;
                    }
                }
                if ($hasowner) {
                    try {
                        $this->create_team($course->id, $groupobjectrec->objectid, $appid);
                    } catch (\Exception $e) {
                        $this->mtrace('Could not create team for course #' . $course->id . '. Reason: ' . $e->getMessage());
                    }
                } else {
                    $this->mtrace('Skip creating team for course #' . $course->id . '. Reason: No owner');
                }
            }
        }
    }