moodle_local_treestudyplan/classes/cascadecohortsync.php
2023-06-26 21:44:31 +02:00

216 lines
11 KiB
PHP

<?php
namespace local_treestudyplan;
require_once($CFG->libdir.'/externallib.php');
use \local_treestudyplan\studyplan;
use \local_treestudyplan\local\helpers\debugger;
class cascadecohortsync {
private const METHOD = "cohort";
private $studyplan;
private $studyplanid;
private $debug;
function __construct(studyplan $studyplan){
$this->studyplan = $studyplan;
$this->studyplanid = $studyplan->id();
$this->debug = new debugger("/tmp/treestudyplan.log","ccsync/{$studyplan->shortname()}/{$this->studyplanid}");
}
static private function array_remove_value($array,$value){
$a = [];
foreach($array as $v){
if($v != $value){
$a[] = $v;
}
}
return $a;
}
static function uploadenrolmentmethods_get_group($courseid, $groupname) {
// Function shamelessly copied from tool/uploadenrolmentmethods/locallib.php
global $DB, $CFG;
require_once($CFG->dirroot.'/group/lib.php');
// Check to see if the group name already exists in this course.
if ($DB->record_exists('groups', array('name' => $groupname, 'courseid' => $courseid))) {
$group = $DB->get_record('groups', array('name' => $groupname, 'courseid' => $courseid));
return $group->id;
}
// The named group doesn't exist, so create a new one in the course.
$groupdata = new \stdClass();
$groupdata->courseid = $courseid;
$groupdata->name = $groupname;
$groupid = groups_create_group($groupdata);
return $groupid;
}
function sync(){
global $DB;
/* Explainer:
This script uses {enrol}.customtext4 to store a json array of all studyplans that need this cohort sync to exist.
Since the cohort-sync enrolment method uses only customint1 and customint2, this is a safe place to store the data.
(Should the cohortsync script at any future time be modified to use customtext fields, it is still extremely unlikely that
customtext4 will be used.)
Because of the overhead involved in keeping an extra table up to date and clean it up if cohort syncs are removed outside of this script,
it was determined to be the simplest and cleanest solution.
*/
$enrol = enrol_get_plugin(self::METHOD);
$this->debug->dump($enrol,"Enrol");
// Find the courses that need to be synced to the associated cohorts
$courseids = $this->studyplan->get_linked_course_ids();
$this->debug->dump($courseids,"courseids");
// And find the cohorts that are linked to this studyplan.
$cohortids = $this->studyplan->get_linked_cohort_ids();
$this->debug->dump($cohortids,"cohortids");
// Next, for each course that is linked:
foreach($courseids as $courseid){
$course = get_course($courseid);
$this->debug->write("Processing Course {$courseid} {$course->shortname}");
// first create any nonexistent links
foreach($cohortids as $cohortid){
$cohort = $DB->get_record('cohort',['id'=>$cohortid]);
$this->debug->write("Processing cohort {$cohortid} {$cohort->shortname}");
$instanceparams = [
'courseid' => $courseid,
'customint1' => $cohortid,
'enrol' => self::METHOD,
'roleid' => get_config("local_treestudyplan","csync_roleid"),
];
$instancenewparams = [
'customint1' => $cohortid,
'enrol' => self::METHOD,
'roleid' => get_config("local_treestudyplan","csync_roleid"),
];
$this->debug->dump($instanceparams,"instanceparams");
$this->debug->dump($instancenewparams,"instancenewparams");
// Create group:
// 1: check if a link exists
// If not, make it (maybe use some of the custom text to list the studyplans involved)
if ($instance = $DB->get_record('enrol', $instanceparams)) {
$this->debug->write("Instance exists");
// it already exists
// check if this studyplan is already referenced in customtext4 in json format
//TODO: Check this code - Maybe add option to not remember manually added stuff
$plans = json_decode($instance->customtext4);
if($plans == false || !is_array(($plans))){
// If the data was not an array (null or garbled), count it as manually added
// This will prevent it's deletion upon
if(get_config("local_treestudyplan","csync_remember_manual_csync")){
$plans = ["manual"];
} else {
$plans = [];
}
}
$this->debug->dump($plans,"plans");
if(!in_array($this->studyplanid ,$plans)){
// if not, add it to the reference
$this->debug->write("Adding this plan to the list");
$plans[] = $this->studyplanid;
$enrol->update_instance($instance,(object)["customtext4"=>json_encode($plans)]);
}
} else {
$this->debug->write("New instance should be made");
// If method members should be added to a group, create it or get its ID.
if (get_config("local_treestudyplan","csync_creategroup")) {
// Make or get new new cohort group - but only on creating of instances
$groupname = $cohort->name." ".strtolower(\get_string('defaultgroupname', 'core_group'));
$this->debug->write("Adding group {$groupname} for this method");
// and make sure the
$instancenewparams['customint2'] = self::uploadenrolmentmethods_get_group($courseid, $groupname);
}
if ($instanceid = $enrol->add_instance($course, $instancenewparams)) {
// also record the (as of yet only) studyplans id requiring this association
// in the customtext4 field in json format
$this->debug->write("Instance ({$instanceid} created. Updateing instance with studyplan id");
$instance = $DB->get_record('enrol', array('id' => $instanceid));
$enrol->update_instance($instance,(object)["customtext4"=>json_encode([$this->studyplanid])]);
$this->debug->write("Synchronize the enrolment");
// Successfully added a valid new instance, so now instantiate it.
// First synchronise the enrolment.
$cohorttrace = new \null_progress_trace();
$result = enrol_cohort_sync($cohorttrace, $cohortid);
if($result > 0){
$this->debug->write("Error during 'enrol_cohort_sync': code {$result}");
}
$cohorttrace->finished();
} else {
// Instance not added for some reason, so report an error somewhere
// (or not)
$this->debug->write("Error - instance not added for some reason");
}
}
}
// 2: Check if there are cohort links for this studyplan in this course that should be removed
// A: Check if there are cohort links that are no longer related to this studyplan
// B: Check if these links are valid through another studyplan...
// If no one uses the link anymore, deactivate it...
// INFO: This does not remove the sync from courses that are unlinked from a studplan. But maybe we do not want that anyway
// since it is generally a good idea to keep student access to courses available
if(get_config("local_treestudyplan","csync_autoremove")){
// Only try the autoremove if the option is enabled
$this->debug->write("Autoremove scan for course {$courseid} {$course->shortname}");
// find all cohort syncs for this course
$searchparams = [
'courseid' => $courseid,
'enrol' => self::METHOD,
'roleid' => get_config("local_treestudyplan","csync_roleid"),
];
$records = $DB->get_records("enrol",$searchparams);
foreach($records as $instance){
if(!empty($instance->customtext4)){ // only check the records that have studyplan information in the customtext4 field
// first check if the cohort is not one of the cohort id's we have associated
if(!in_array($instance->customint1,$cohortids)){
$this->debug->write("Found cohort sync instance that is not currently liked to the studyplan: {$instance->id}");
// So it may or may not need to be removed
$plans = json_decode($instance->customtext4);
$this->debug->dump($plans,"Plans attachted to instance {$instance->id}");
if($plans !== null && is_array($plans)){
//if a valid array is not returned, better leave it be, we don't want to mess with it
// otherwise, check if we should remove it
if(in_array($this->studyplanid,$plans)){
$this->debug->write("Found this studyplan in the id list - removing from list");
//if this plan was referenced before
// first remove the link
$fplans = self::array_remove_value($plans,$this->studyplanid);
$this->debug->dump($fplans,"Plans proposed to attach to instance {$instance->id}");
if(count($fplans) == 0){
// delete the sync if there are no studyplan references left
$this->debug->write("No references are left, removing instance");
$enrol->delete_instance($instance);
} else {
// otherwise just update the references so this studyplan is no longer linked
$this->debug->write("Still references left in the list, updating list...");
$enrol->update_instance($instance,(object)["customtext4"=>json_encode($fplans)]);
}
}
}
}
}
}
}
}
$this->debug->write("Cascading complete");
}
}