Moodle code style fixes part 1
This commit is contained in:
		
							parent
							
								
									ab8a3c6f84
								
							
						
					
					
						commit
						1a3df05195
					
				
					 63 changed files with 4467 additions and 3885 deletions
				
			
		
							
								
								
									
										42
									
								
								build.php
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								build.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,13 +1,35 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
define('CLI_SCRIPT', true);
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
$plugin = new stdClass;
 | 
			
		||||
include('version.php');
 | 
			
		||||
require_once('version.php');
 | 
			
		||||
 | 
			
		||||
$a = explode("_",$plugin->component,2);
 | 
			
		||||
$a = explode("_", $plugin->component, 2);
 | 
			
		||||
$plugin->type = $a[0];
 | 
			
		||||
$plugin->name = $a[1];
 | 
			
		||||
 | 
			
		||||
$exclude_paths = [
 | 
			
		||||
    "build",     // dir for build zip files
 | 
			
		||||
$excludepaths = [
 | 
			
		||||
    "build",     // Dir for build zip files.
 | 
			
		||||
    "build/*",
 | 
			
		||||
    "build.*",
 | 
			
		||||
    "vuemode.sh",
 | 
			
		||||
| 
						 | 
				
			
			@ -19,28 +41,28 @@ $exclude_paths = [
 | 
			
		|||
    "*.zip",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
// Determine some paths 
 | 
			
		||||
// Determine some paths.
 | 
			
		||||
$wd = realpath(dirname(__FILE__));
 | 
			
		||||
$parent = dirname($wd);
 | 
			
		||||
$plugindirname = basename($wd);
 | 
			
		||||
$builddir = $wd."/"."build";
 | 
			
		||||
$zipname = $builddir."/"."{$plugin->name}-{$plugin->version}.zip";
 | 
			
		||||
 | 
			
		||||
// create the exclude line
 | 
			
		||||
// Create the exclude line.
 | 
			
		||||
$exclude = "-x ";
 | 
			
		||||
foreach($exclude_paths as $x){
 | 
			
		||||
foreach ($excludepaths as $x) {
 | 
			
		||||
    $exclude .= "'{$plugindirname}/{$x}' ";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(!is_dir($builddir)){
 | 
			
		||||
if (!is_dir($builddir)) {
 | 
			
		||||
    mkdir($builddir);
 | 
			
		||||
    if(!is_dir($builddir)){
 | 
			
		||||
    if (!is_dir($builddir)) {
 | 
			
		||||
        print("Cannot access dir '{$builddir}' to store zip files\n");
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(file_exists($zipfile)){
 | 
			
		||||
if (file_exists($zipfile)) {
 | 
			
		||||
    print("Zip file '{$zipfile}' already exists. Exiting...\n");
 | 
			
		||||
    exit(1);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										158
									
								
								cfg_grades.php
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								cfg_grades.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,12 +1,32 @@
 | 
			
		|||
<?php
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/adminlib.php');
 | 
			
		||||
admin_externalpage_setup("local_treestudyplan_gradeconfig");
 | 
			
		||||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
// Check if user has capability to manage this
 | 
			
		||||
// Check if user has capability to manage this.
 | 
			
		||||
require_capability('local/treestudyplan:configure', $systemcontext);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -18,104 +38,103 @@ $scales = \grade_scale::fetch_all_global();
 | 
			
		|||
$mappings = $DB->get_records(GRADECFG_TABLE);
 | 
			
		||||
$scale_cfgs = [];
 | 
			
		||||
$grade_cfgs = [];
 | 
			
		||||
foreach($mappings as $cfg){
 | 
			
		||||
    if(!empty($cfg->scale_id)){
 | 
			
		||||
foreach ($mappings as $cfg) {
 | 
			
		||||
    if (!empty($cfg->scale_id)) {
 | 
			
		||||
        $scale_cfgs[$cfg->scale_id] = $cfg;
 | 
			
		||||
    }
 | 
			
		||||
    elseif(!empty($cfg->grade_points)){
 | 
			
		||||
    else if (!empty($cfg->grade_points)) {
 | 
			
		||||
        $grade_cfgs[$cfg->grade_points] = $cfg;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->header();
 | 
			
		||||
 | 
			
		||||
if($_POST["action"] == "update"){
 | 
			
		||||
    // First loop through the scales to see which need to be updated
 | 
			
		||||
    foreach($scales as $scale)
 | 
			
		||||
    {
 | 
			
		||||
        if(array_key_exists($scale->id,$scale_cfgs)){
 | 
			
		||||
if ($_POST["action"] == "update") {
 | 
			
		||||
    // First loop through the scales to see which need to be updated.
 | 
			
		||||
    foreach ($scales as $scale) {
 | 
			
		||||
        if (array_key_exists($scale->id, $scale_cfgs)) {
 | 
			
		||||
            $scalecfg = $scale_cfgs[$scale->id];
 | 
			
		||||
 | 
			
		||||
            $needupdate = false;
 | 
			
		||||
            foreach(["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
            foreach (["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
                $key = "s_{$scale->id}_{$handle}";
 | 
			
		||||
                if(array_key_exists($key,$_POST) && is_numeric($_POST[$key])){
 | 
			
		||||
                if (array_key_exists($key, $_POST) && is_numeric($_POST[$key])) {
 | 
			
		||||
                    $value = intval($_POST[$key]);
 | 
			
		||||
                    if($value != $scalecfg->$handle){
 | 
			
		||||
                    if ($value != $scalecfg->$handle) {
 | 
			
		||||
                        $scalecfg->$handle = $value;
 | 
			
		||||
                        $needupdate = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if($needupdate){
 | 
			
		||||
                $DB->update_record(GRADECFG_TABLE,$scalecfg);
 | 
			
		||||
            if ($needupdate) {
 | 
			
		||||
                $DB->update_record(GRADECFG_TABLE, $scalecfg);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            $scalecfg = (object)[ "scale_id" => $scale->id,];
 | 
			
		||||
            $scalecfg = (object)[ "scale_id" => $scale->id, ];
 | 
			
		||||
            $requireinsert = false;
 | 
			
		||||
            foreach(["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
            foreach (["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
                $key = "s_{$scale->id}_{$handle}";
 | 
			
		||||
                if(array_key_exists($key,$_POST) && is_numeric($_POST[$key])){
 | 
			
		||||
                if (array_key_exists($key, $_POST) && is_numeric($_POST[$key])) {
 | 
			
		||||
                    $scalecfg->$handle = intval($_POST[$key]);
 | 
			
		||||
                    $requireinsert = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if($requireinsert){
 | 
			
		||||
                // Insert into database and add to the list of scale configs
 | 
			
		||||
                $id = $DB->insert_record(GRADECFG_TABLE,$scalecfg);
 | 
			
		||||
                $scalecfg = $DB->get_record(GRADECFG_TABLE,['id' => $id]);
 | 
			
		||||
            if ($requireinsert) {
 | 
			
		||||
                // Insert into database and add to the list of scale configs.
 | 
			
		||||
                $id = $DB->insert_record(GRADECFG_TABLE, $scalecfg);
 | 
			
		||||
                $scalecfg = $DB->get_record(GRADECFG_TABLE, ['id' => $id]);
 | 
			
		||||
                $scale_cfgs[$id] = $scalecfg;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // Now, loop through the gradepoints to parse 
 | 
			
		||||
    // Now, loop through the gradepoints to parse .
 | 
			
		||||
    $deletelist = [];
 | 
			
		||||
    foreach($grade_cfgs as $gradecfg){
 | 
			
		||||
    foreach ($grade_cfgs as $gradecfg) {
 | 
			
		||||
        $deletekey = "g_{$gradecfg->grade_points}_delete";
 | 
			
		||||
        if(array_key_exists($deletekey,$_POST) && boolval($_POST[$deletekey]) === true){
 | 
			
		||||
            $DB->delete_records(GRADECFG_TABLE,["id" => $gradecfg->id]);
 | 
			
		||||
        if (array_key_exists($deletekey, $_POST) && boolval($_POST[$deletekey]) === true) {
 | 
			
		||||
            $DB->delete_records(GRADECFG_TABLE, ["id" => $gradecfg->id]);
 | 
			
		||||
            $deletelist[] = $gradecfg;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            foreach(["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
            foreach (["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
                $key = "g_{$gradecfg->grade_points}_{$handle}";
 | 
			
		||||
                if(array_key_exists($key,$_POST) && is_numeric($_POST[$key])){
 | 
			
		||||
                if (array_key_exists($key, $_POST) && is_numeric($_POST[$key])) {
 | 
			
		||||
                    $gradecfg->$handle = floatval($_POST[$key]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $DB->update_record(GRADECFG_TABLE,$gradecfg);
 | 
			
		||||
            // reload to ensure proper rounding is done
 | 
			
		||||
            $grade_cfgs[$gradecfg->grade_points] = $DB->get_record(GRADECFG_TABLE,['id' => $gradecfg->id]);
 | 
			
		||||
            $DB->update_record(GRADECFG_TABLE, $gradecfg);
 | 
			
		||||
            // reload to ensure proper rounding is done.
 | 
			
		||||
            $grade_cfgs[$gradecfg->grade_points] = $DB->get_record(GRADECFG_TABLE, ['id' => $gradecfg->id]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    foreach($deletelist as $gradeconfig){
 | 
			
		||||
    foreach ($deletelist as $gradeconfig) {
 | 
			
		||||
        unset($grade_cfgs[$gradecfg->grade_points]);
 | 
			
		||||
    }
 | 
			
		||||
    unset($deletelist);
 | 
			
		||||
 | 
			
		||||
    // And add an optionally existing new gradepoint setting
 | 
			
		||||
    if(array_key_exists("g_new_gradepoints",$_POST) && !empty($_POST["g_new_gradepoints"])  && is_numeric($_POST["g_new_gradepoints"]) ){
 | 
			
		||||
    // And add an optionally existing new gradepoint setting.
 | 
			
		||||
    if (array_key_exists("g_new_gradepoints", $_POST) && !empty($_POST["g_new_gradepoints"])  && is_numeric($_POST["g_new_gradepoints"]) ) {
 | 
			
		||||
        $gp = intval($_POST["g_new_gradepoints"]);
 | 
			
		||||
        if(!array_key_exists($gp,$grade_cfgs)){
 | 
			
		||||
        if (!array_key_exists($gp, $grade_cfgs)) {
 | 
			
		||||
            $gradecfg = (object)[ "grade_points" => $gp];
 | 
			
		||||
            $requireinsert = false;
 | 
			
		||||
            foreach(["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
            foreach (["min_progress", "min_completed"] as $handle) {
 | 
			
		||||
                $key = "g_new_{$handle}";
 | 
			
		||||
                if(array_key_exists($key,$_POST) && is_numeric($_POST[$key])){
 | 
			
		||||
                if (array_key_exists($key, $_POST) && is_numeric($_POST[$key])) {
 | 
			
		||||
                    $gradecfg->$handle = floatval($_POST[$key]);
 | 
			
		||||
                    $requireinsert = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if($requireinsert){
 | 
			
		||||
                // Insert into database and add to the list of grade configs
 | 
			
		||||
                $id = $DB->insert_record(GRADECFG_TABLE,$gradecfg);
 | 
			
		||||
                // reload to ensure proper rounding is done
 | 
			
		||||
                $gradecfg = $DB->get_record(GRADECFG_TABLE,['id' => $id]);
 | 
			
		||||
            if ($requireinsert) {
 | 
			
		||||
                // Insert into database and add to the list of grade configs.
 | 
			
		||||
                $id = $DB->insert_record(GRADECFG_TABLE, $gradecfg);
 | 
			
		||||
                // reload to ensure proper rounding is done.
 | 
			
		||||
                $gradecfg = $DB->get_record(GRADECFG_TABLE, ['id' => $id]);
 | 
			
		||||
                $grade_cfgs[$id] = $gradecfg;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -127,65 +146,64 @@ if($_POST["action"] == "update"){
 | 
			
		|||
 | 
			
		||||
//process all available scales and load the current configuration for it.
 | 
			
		||||
$data = [];
 | 
			
		||||
foreach($scales as $scale)
 | 
			
		||||
{
 | 
			
		||||
foreach ($scales as $scale) {
 | 
			
		||||
    $scale->load_items();
 | 
			
		||||
    $scalecfg = null;
 | 
			
		||||
    if(array_key_exists($scale->id,$scale_cfgs)){
 | 
			
		||||
    if (array_key_exists($scale->id, $scale_cfgs)) {
 | 
			
		||||
        $scalecfg = $scale_cfgs[$scale->id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $attrs_c = ['value' => '','disabled' => 'disabled', ];
 | 
			
		||||
    $attrs_p = ['value' => '','disabled' => 'disabled', ];
 | 
			
		||||
    $attrs_c = ['value' => '', 'disabled' => 'disabled', ];
 | 
			
		||||
    $attrs_p = ['value' => '', 'disabled' => 'disabled', ];
 | 
			
		||||
 | 
			
		||||
    if(!isset($scalecfg) || $scalecfg->min_completed == ""){
 | 
			
		||||
    if (!isset($scalecfg) || $scalecfg->min_completed == "") {
 | 
			
		||||
        $attrs_c["selected"] = "selected";
 | 
			
		||||
    }
 | 
			
		||||
    if(!isset($scalecfg) || $scalecfg->min_progress == ""){
 | 
			
		||||
    if (!isset($scalecfg) || $scalecfg->min_progress == "") {
 | 
			
		||||
        $attrs_p["selected"] = "selected";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $options_completed = html_writer::tag("option",get_string('select_scaleitem','local_treestudyplan'),$attrs_c);
 | 
			
		||||
    $options_progress = html_writer::tag("option",get_string('select_scaleitem','local_treestudyplan'),$attrs_p);
 | 
			
		||||
    $key = 1; // Start counting by one, as used in sum aggregations
 | 
			
		||||
    $options_completed = html_writer::tag("option", get_string('select_scaleitem', 'local_treestudyplan'), $attrs_c);
 | 
			
		||||
    $options_progress = html_writer::tag("option", get_string('select_scaleitem', 'local_treestudyplan'), $attrs_p);
 | 
			
		||||
    $key = 1; // Start counting by one, as used in sum aggregations.
 | 
			
		||||
 | 
			
		||||
    foreach($scale->scale_items as $value){
 | 
			
		||||
    foreach ($scale->scale_items as $value) {
 | 
			
		||||
        $attrs_c = ["value" => $key];
 | 
			
		||||
        $attrs_p = ["value" => $key];
 | 
			
		||||
 | 
			
		||||
        if(isset($scalecfg)){
 | 
			
		||||
            if(intval($scalecfg->min_completed) == $key){
 | 
			
		||||
        if (isset($scalecfg)) {
 | 
			
		||||
            if (intval($scalecfg->min_completed) == $key) {
 | 
			
		||||
                $attrs_c["selected"] = "selected";
 | 
			
		||||
            }
 | 
			
		||||
            if(intval($scalecfg->min_progress) == $key){
 | 
			
		||||
            if (intval($scalecfg->min_progress) == $key) {
 | 
			
		||||
                $attrs_p["selected"] = "selected";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $options_progress .= html_writer::tag("option",$value,$attrs_p);
 | 
			
		||||
        $options_completed .= html_writer::tag("option",$value,$attrs_c);
 | 
			
		||||
        $options_progress .= html_writer::tag("option", $value, $attrs_p);
 | 
			
		||||
        $options_completed .= html_writer::tag("option", $value, $attrs_c);
 | 
			
		||||
        $key++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	$row = [];
 | 
			
		||||
	$row[] = $scale->name;
 | 
			
		||||
	//$row[] = html_writer::tag("select", $options_progress, ['name' => "s_{$scale->id}_min_progress",'autocomplete' => 'off']) ;
 | 
			
		||||
	$row[] = html_writer::tag("select", $options_completed, ['name' => "s_{$scale->id}_min_completed",'autocomplete' => 'off']) ;
 | 
			
		||||
	//$row[] = html_writer::tag("select", $options_progress, ['name' => "s_{$scale->id}_min_progress", 'autocomplete' => 'off']) ;.
 | 
			
		||||
	$row[] = html_writer::tag("select", $options_completed, ['name' => "s_{$scale->id}_min_completed", 'autocomplete' => 'off']) ;
 | 
			
		||||
	$data[] = $row;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
print html_writer::start_tag("form",["method" => "post",]);
 | 
			
		||||
print html_writer::start_tag("form", ["method" => "post", ]);
 | 
			
		||||
print html_writer::tag("input", null, ['name' => "action", 'value' => 'update', 'type' => 'hidden']);
 | 
			
		||||
 | 
			
		||||
$table = new html_table();
 | 
			
		||||
$table->id = "";
 | 
			
		||||
$table->attributes['class'] = 'generaltable m-roomtable';
 | 
			
		||||
$table->tablealign = 'center';
 | 
			
		||||
$table->summary = '';//get_string('uploadtimetable_preview', 'local_chronotable');
 | 
			
		||||
$table->summary = '';//get_string('uploadtimetable_preview', 'local_chronotable');.
 | 
			
		||||
$table->head = [];
 | 
			
		||||
$table->data = $data;
 | 
			
		||||
$table->head[] = get_string('scale');
 | 
			
		||||
//$table->head[] = get_string('min_progress', 'local_treestudyplan');
 | 
			
		||||
//$table->head[] = get_string('min_progress', 'local_treestudyplan');.
 | 
			
		||||
$table->head[] = get_string('min_completed', 'local_treestudyplan');
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->heading(get_string('cfg_grades_desc_head', 'local_treestudyplan'));
 | 
			
		||||
| 
						 | 
				
			
			@ -194,10 +212,10 @@ print $OUTPUT->heading(get_string('cfg_grades_scales', 'local_treestudyplan'));
 | 
			
		|||
print html_writer::tag('div', html_writer::table($table), ['class'=>'flexible-wrap']);
 | 
			
		||||
 | 
			
		||||
$data = [];
 | 
			
		||||
foreach($grade_cfgs as $g){
 | 
			
		||||
foreach ($grade_cfgs as $g) {
 | 
			
		||||
    $row = [];
 | 
			
		||||
	$row[] = $g->grade_points;
 | 
			
		||||
//    $row[] = html_writer::tag("input", null, ['name' => "g_{$g->grade_points}_min_progress", 'value' => "{$g->min_progress}", 'type' => 'text', "class" => "float", 'autocomplete' => 'off']) ;
 | 
			
		||||
//    $row[] = html_writer::tag("input", null, ['name' => "g_{$g->grade_points}_min_progress", 'value' => "{$g->min_progress}", 'type' => 'text', "class" => "float", 'autocomplete' => 'off']) ;.
 | 
			
		||||
    $row[] = html_writer::tag("input", null, ['name' => "g_{$g->grade_points}_min_completed", 'value' => "{$g->min_completed}", 'type' => 'text', "class" => "float", 'autocomplete' => 'off']) ;
 | 
			
		||||
    $row[] = html_writer::tag("input", null, ['name' => "g_{$g->grade_points}_delete", 'type' => 'checkbox', ]) ;
 | 
			
		||||
    $data[] = $row;
 | 
			
		||||
| 
						 | 
				
			
			@ -205,8 +223,8 @@ foreach($grade_cfgs as $g){
 | 
			
		|||
 | 
			
		||||
$row = [];
 | 
			
		||||
$row[] = html_writer::tag("input", null, ['name' => "g_new_gradepoints", 'value' => '', 'type' => 'number', 'min' => '0', 'pattern' => '/d+', 'step' => '1', 'autocomplete' => 'off']);
 | 
			
		||||
//$row[] = html_writer::tag("input", null, ['name' => "g_new_min_progress", 'value' => '', 'type' => 'text', "class" => "float", 'autocomplete' => 'off']) ;
 | 
			
		||||
$row[] = html_writer::tag("input", null, ['name' => "g_new_min_completed", 'value' => '', 'type' => 'text',"class" => "float", 'autocomplete' => 'off']) ;
 | 
			
		||||
//$row[] = html_writer::tag("input", null, ['name' => "g_new_min_progress", 'value' => '', 'type' => 'text', "class" => "float", 'autocomplete' => 'off']) ;.
 | 
			
		||||
$row[] = html_writer::tag("input", null, ['name' => "g_new_min_completed", 'value' => '', 'type' => 'text', "class" => "float", 'autocomplete' => 'off']) ;
 | 
			
		||||
 | 
			
		||||
$data[] = $row;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -215,13 +233,13 @@ $table = new html_table();
 | 
			
		|||
$table->id = "";
 | 
			
		||||
$table->attributes['class'] = 'generaltable m-roomtable';
 | 
			
		||||
$table->tablealign = 'center';
 | 
			
		||||
$table->summary = '';//get_string('uploadtimetable_preview', 'local_chronotable');
 | 
			
		||||
$table->summary = '';//get_string('uploadtimetable_preview', 'local_chronotable');.
 | 
			
		||||
$table->head = [];
 | 
			
		||||
$table->data = $data;
 | 
			
		||||
$table->head[] = get_string('grade_points', 'local_treestudyplan');
 | 
			
		||||
//$table->head[] = get_string('min_progress', 'local_treestudyplan');
 | 
			
		||||
//$table->head[] = get_string('min_progress', 'local_treestudyplan');.
 | 
			
		||||
$table->head[] = get_string('min_completed', 'local_treestudyplan');
 | 
			
		||||
$table->head[] = get_string('delete',);
 | 
			
		||||
$table->head[] = get_string('delete', );
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->heading(get_string('cfg_grades_grades', 'local_treestudyplan'));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -7,19 +28,19 @@ abstract class aggregator {
 | 
			
		|||
    private const FALLBACK = "bistate";
 | 
			
		||||
    private static $mod_supported = [];
 | 
			
		||||
 | 
			
		||||
    public static function supported($mod){
 | 
			
		||||
        if(!array_key_exists($mod,self::$mod_supported)){
 | 
			
		||||
    public static function supported($mod) {
 | 
			
		||||
        if (!array_key_exists($mod, self::$mod_supported)) {
 | 
			
		||||
            self::$mod_supported[$mod] = class_exists(self::aggregator_name($mod));
 | 
			
		||||
        }
 | 
			
		||||
        return self::$mod_supported[$mod];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function aggregator_name($mod){
 | 
			
		||||
    private static function aggregator_name($mod) {
 | 
			
		||||
        return "\local_treestudyplan\\local\\aggregators\\{$mod}_aggregator";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list(){
 | 
			
		||||
        // static list, since we'd need to implement a lot of static data for new aggregation methods anyway
 | 
			
		||||
    public static function list() {
 | 
			
		||||
        // static list, since we'd need to implement a lot of static data for new aggregation methods anyway.
 | 
			
		||||
        // and this is faster than any dynamic method.
 | 
			
		||||
        return [
 | 
			
		||||
            "core", # use moodle core completion
 | 
			
		||||
| 
						 | 
				
			
			@ -28,8 +49,8 @@ abstract class aggregator {
 | 
			
		|||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function create($mod,$configstr){
 | 
			
		||||
        if(self::supported($mod)){
 | 
			
		||||
    public static function create($mod, $configstr) {
 | 
			
		||||
        if (self::supported($mod)) {
 | 
			
		||||
            $ag_class = self::aggregator_name($mod);
 | 
			
		||||
            return new $ag_class($configstr);
 | 
			
		||||
        } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +58,12 @@ abstract class aggregator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function createOrDefault($mod,$configstr){
 | 
			
		||||
    public static function createOrDefault($mod, $configstr) {
 | 
			
		||||
        try {
 | 
			
		||||
            return self::create($mod,$configstr);
 | 
			
		||||
            return self::create($mod, $configstr);
 | 
			
		||||
        }
 | 
			
		||||
        catch(\ValueError $x){
 | 
			
		||||
            return self::create(self::FALLBACK,"");
 | 
			
		||||
        catch(\ValueError $x) {
 | 
			
		||||
            return self::create(self::FALLBACK, "");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,55 +80,55 @@ abstract class aggregator {
 | 
			
		|||
 | 
			
		||||
    public abstract function grade_completion(gradeinfo $gradeinfo, $userid);
 | 
			
		||||
 | 
			
		||||
    // Aggregation method makes use of "required grades" in a course/module
 | 
			
		||||
    // Aggregation method makes use of "required grades" in a course/module.
 | 
			
		||||
    public abstract function useRequiredGrades();
 | 
			
		||||
    // Aggregation method makes use of 
 | 
			
		||||
    // Aggregation method makes use of .
 | 
			
		||||
    public abstract function useItemConditions();
 | 
			
		||||
 | 
			
		||||
    // Whether the aggregation method uses core_completion, or treestudyplan custom completion
 | 
			
		||||
    public function usecorecompletioninfo(){ 
 | 
			
		||||
    // Whether the aggregation method uses core_completion, or treestudyplan custom completion.
 | 
			
		||||
    public function usecorecompletioninfo() {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Parameter editing functions - override in child class to implement parameter config for aggregation
 | 
			
		||||
    // Parameter editing functions - override in child class to implement parameter config for aggregation.
 | 
			
		||||
 | 
			
		||||
    // Return the current configuration string
 | 
			
		||||
    // Return the current configuration string.
 | 
			
		||||
    public function config_string() {
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function basic_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function basic_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "useRequiredGrades" => new \external_value(PARAM_BOOL, 'id of studyplan'),
 | 
			
		||||
            "useItemConditions" => new \external_value(PARAM_BOOL, 'name of studyplan'),
 | 
			
		||||
        ],"Aggregator requirements",$value);
 | 
			
		||||
        ], "Aggregator requirements", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function basic_model(){
 | 
			
		||||
    public function basic_model() {
 | 
			
		||||
        return [
 | 
			
		||||
            "useRequiredGrades" => $this->useRequiredGrades(),
 | 
			
		||||
            "useItemConditions" => $this->useItemConditions(),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function list_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return  new \external_multiple_structure(new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_TEXT, 'id of aggregator'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'name of agregator'),
 | 
			
		||||
            "deprecated" => new \external_value(PARAM_BOOL, 'if method is deprecated'),
 | 
			
		||||
            "defaultconfig" => new \external_value(PARAM_TEXT, 'default config of agregator'),
 | 
			
		||||
 | 
			
		||||
        ],"Available aggregators",$value));
 | 
			
		||||
        ], "Available aggregators", $value));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list_model(){
 | 
			
		||||
    public static function list_model() {
 | 
			
		||||
 | 
			
		||||
        $list = [];
 | 
			
		||||
        foreach(self::list() as $agid){
 | 
			
		||||
            $a = self::create($agid,""); // create new one with empty config string
 | 
			
		||||
        foreach (self::list() as $agid) {
 | 
			
		||||
            $a = self::create($agid, ""); // create new one with empty config string.
 | 
			
		||||
            $list[] = [
 | 
			
		||||
                'id' => $agid,
 | 
			
		||||
                'name' => get_string("{$agid}_aggregator_title","local_treestudyplan"),
 | 
			
		||||
                'name' => get_string("{$agid}_aggregator_title", "local_treestudyplan"),
 | 
			
		||||
                'deprecated' => $a->isDeprecated(),
 | 
			
		||||
                'defaultconfig' => $a->config_string(),
 | 
			
		||||
            ];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
use local_treestudyplan\local\helpers\webservicehelper;
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +31,7 @@ class associationservice extends \external_api
 | 
			
		|||
    const CAP_EDIT = "local/treestudyplan:editstudyplan";
 | 
			
		||||
    const CAP_VIEW = "local/treestudyplan:viewuserreports";
 | 
			
		||||
 | 
			
		||||
    public static function user_structure(){
 | 
			
		||||
    public static function user_structure() {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'user id'),
 | 
			
		||||
            "username" => new \external_value(PARAM_TEXT, 'username'),
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +42,7 @@ class associationservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function make_user_model($r){
 | 
			
		||||
    public static function make_user_model($r) {
 | 
			
		||||
        return [
 | 
			
		||||
            "id" => $r->id,
 | 
			
		||||
            "username" => $r->username,
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +53,7 @@ class associationservice extends \external_api
 | 
			
		|||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function cohort_structure(){
 | 
			
		||||
    public static function cohort_structure() {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'cohort id'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'name'),
 | 
			
		||||
| 
						 | 
				
			
			@ -48,12 +69,12 @@ class associationservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function make_cohort_model($r){
 | 
			
		||||
    public static function make_cohort_model($r) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $ctx = \context::instance_by_id($r->contextid);
 | 
			
		||||
        $ctxPath = array_reverse($ctx->get_parent_context_ids(true));
 | 
			
		||||
        if(count($ctxPath) > 1 && $ctxPath[0] == 1) {
 | 
			
		||||
        if (count($ctxPath) > 1 && $ctxPath[0] == 1) {
 | 
			
		||||
            array_shift($ctxPath);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -64,10 +85,10 @@ class associationservice extends \external_api
 | 
			
		|||
            "description" => $r->description,
 | 
			
		||||
            "visible" => $r->visible,
 | 
			
		||||
            "context" => [
 | 
			
		||||
                "name" => $ctx->get_context_name(false,false),
 | 
			
		||||
                "shortname" => $ctx->get_context_name(false,true),
 | 
			
		||||
                "path" => array_map(function($c){ return \context::instance_by_id($c)->get_context_name(false,false);},$ctxPath),
 | 
			
		||||
                "shortpath" => array_map(function($c){ return \context::instance_by_id($c)->get_context_name(false,true);},$ctxPath),
 | 
			
		||||
                "name" => $ctx->get_context_name(false, false),
 | 
			
		||||
                "shortname" => $ctx->get_context_name(false, true),
 | 
			
		||||
                "path" => array_map(function($c) { return \context::instance_by_id($c)->get_context_name(false, false);}, $ctxPath),
 | 
			
		||||
                "shortpath" => array_map(function($c) { return \context::instance_by_id($c)->get_context_name(false, true);}, $ctxPath),
 | 
			
		||||
            ]
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,33 +111,33 @@ class associationservice extends \external_api
 | 
			
		|||
        return new \external_multiple_structure(self::cohort_structure());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	public static function list_cohort($like='',$exclude_id=null,$context_id=1) 
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function list_cohort($like='', $exclude_id=null, $context_id=1)
 | 
			
		||||
	{
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        // Only allow this if the user has the right to edit in this context
 | 
			
		||||
        // Only allow this if the user has the right to edit in this context.
 | 
			
		||||
        $context = webservicehelper::find_context($context_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $context);
 | 
			
		||||
 | 
			
		||||
        $pattern = "%{$like}%";
 | 
			
		||||
 | 
			
		||||
        $params = ["pattern_nm" => $pattern,"pattern_id" => $pattern,];
 | 
			
		||||
        $params = ["pattern_nm" => $pattern, "pattern_id" => $pattern, ];
 | 
			
		||||
 | 
			
		||||
        $sql =  "SELECT c.* from {cohort} c LEFT JOIN {local_treestudyplan_cohort} j ON c.id = j.cohort_id";
 | 
			
		||||
        $sql .= " WHERE c.visible = 1 AND(name LIKE :pattern_nm OR idnumber LIKE :pattern_id)";
 | 
			
		||||
        if(isset($exclude_id) && is_numeric($exclude_id)){
 | 
			
		||||
        if (isset($exclude_id) && is_numeric($exclude_id)) {
 | 
			
		||||
            $sql .= " AND  (j.studyplan_id IS NULL OR j.studyplan_id != :exclude_id)";
 | 
			
		||||
            $params['exclude_id'] = $exclude_id;
 | 
			
		||||
        }
 | 
			
		||||
        if($context_id > 1){ // system context returns all cohorts, including system cohorts
 | 
			
		||||
            // otherwise, 
 | 
			
		||||
        if ($context_id > 1) { // system context returns all cohorts, including system cohorts.
 | 
			
		||||
            // otherwise, .
 | 
			
		||||
            $sql .= " AND contextid = :context_id";
 | 
			
		||||
            $params['context_id'] = $context_id;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $cohorts = [];
 | 
			
		||||
        $rs = $DB->get_recordset_sql($sql,$params);
 | 
			
		||||
        $rs = $DB->get_recordset_sql($sql, $params);
 | 
			
		||||
        foreach ($rs as $r) {
 | 
			
		||||
            $cohorts[] = static::make_cohort_model($r);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -138,14 +159,14 @@ class associationservice extends \external_api
 | 
			
		|||
        return new \external_multiple_structure(self::user_structure());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	public static function find_user($like,$exclude_id=null,$context_id=1) 
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function find_user($like, $exclude_id=null, $context_id=1)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        // Only allow this if the user has the right to edit in this context (using system rights would make things more confusing)
 | 
			
		||||
        // Only allow this if the user has the right to edit in this context (using system rights would make things more confusing).
 | 
			
		||||
        $context = webservicehelper::find_context($context_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$context);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $context);
 | 
			
		||||
 | 
			
		||||
        $pattern = "%{$like}%";
 | 
			
		||||
        $params = ["pattern_fn" => $pattern,
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +175,7 @@ class associationservice extends \external_api
 | 
			
		|||
                  ];
 | 
			
		||||
        $sql =  "SELECT u.* from {user} u LEFT JOIN {local_treestudyplan_user} j ON u.id = j.user_id";
 | 
			
		||||
        $sql .= " WHERE u.deleted != 1 AND (firstname LIKE :pattern_fn OR lastname LIKE :pattern_ln OR username LIKE :pattern_un)";
 | 
			
		||||
        if(isset($exclude_id) && is_numeric($exclude_id)){
 | 
			
		||||
        if (isset($exclude_id) && is_numeric($exclude_id)) {
 | 
			
		||||
            $sql .= " AND  (j.studyplan_id IS NULL OR j.studyplan_id != :exclude_id)";
 | 
			
		||||
            $params['exclude_id'] = $exclude_id;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -186,16 +207,15 @@ class associationservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	public static function connect_cohort($studyplan_id,$cohort_id) 
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function connect_cohort($studyplan_id, $cohort_id)
 | 
			
		||||
	{
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        if(!$DB->record_exists('local_treestudyplan_cohort', ['studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id]))
 | 
			
		||||
        {
 | 
			
		||||
        if (!$DB->record_exists('local_treestudyplan_cohort', ['studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id])) {
 | 
			
		||||
            $id = $DB->insert_record('local_treestudyplan_cohort', [
 | 
			
		||||
                'studyplan_id' => $studyplan_id,
 | 
			
		||||
                'cohort_id' => $cohort_id,
 | 
			
		||||
| 
						 | 
				
			
			@ -226,16 +246,15 @@ class associationservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	public static function disconnect_cohort($studyplan_id,$cohort_id) 
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function disconnect_cohort($studyplan_id, $cohort_id)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        if($DB->record_exists('local_treestudyplan_cohort', ['studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id]))
 | 
			
		||||
        {
 | 
			
		||||
        if ($DB->record_exists('local_treestudyplan_cohort', ['studyplan_id' => $studyplan_id, 'cohort_id' => $cohort_id])) {
 | 
			
		||||
            $DB->delete_records('local_treestudyplan_cohort', [
 | 
			
		||||
                'studyplan_id' => $studyplan_id,
 | 
			
		||||
                'cohort_id' => $cohort_id,
 | 
			
		||||
| 
						 | 
				
			
			@ -266,16 +285,15 @@ class associationservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	public static function connect_user($studyplan_id,$user_id) 
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function connect_user($studyplan_id, $user_id)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        if(!$DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id]))
 | 
			
		||||
        {
 | 
			
		||||
        if (!$DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id])) {
 | 
			
		||||
            $id = $DB->insert_record('local_treestudyplan_user', [
 | 
			
		||||
                'studyplan_id' => $studyplan_id,
 | 
			
		||||
                'user_id' => $user_id,
 | 
			
		||||
| 
						 | 
				
			
			@ -305,15 +323,14 @@ class associationservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	public static function disconnect_user($studyplan_id,$user_id) 
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function disconnect_user($studyplan_id, $user_id)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        if($DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id]))
 | 
			
		||||
        {
 | 
			
		||||
        if ($DB->record_exists('local_treestudyplan_user', ['studyplan_id' => $studyplan_id, 'user_id' => $user_id])) {
 | 
			
		||||
            $DB->delete_records('local_treestudyplan_user', [
 | 
			
		||||
                'studyplan_id' => $studyplan_id,
 | 
			
		||||
                'user_id' => $user_id,
 | 
			
		||||
| 
						 | 
				
			
			@ -339,20 +356,19 @@ class associationservice extends \external_api
 | 
			
		|||
        return new \external_multiple_structure(self::user_structure());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function associated_users($studyplan_id)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT DISTINCT u.* FROM {user} u INNER JOIN {local_treestudyplan_user} j ON j.user_id = u.id";
 | 
			
		||||
        $sql .= " WHERE j.studyplan_id = :studyplan_id";
 | 
			
		||||
        $rs = $DB->get_recordset_sql($sql, ['studyplan_id' => $studyplan_id]);
 | 
			
		||||
 | 
			
		||||
        $users = [];
 | 
			
		||||
        foreach($rs as $u)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($rs as $u) {
 | 
			
		||||
            $users[] = self::make_user_model($u);
 | 
			
		||||
        }
 | 
			
		||||
        $rs->close();
 | 
			
		||||
| 
						 | 
				
			
			@ -372,19 +388,18 @@ class associationservice extends \external_api
 | 
			
		|||
        return new \external_multiple_structure(self::cohort_structure());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function associated_cohorts($studyplan_id)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT DISTINCT c.* FROM {cohort} c INNER JOIN {local_treestudyplan_cohort} j ON j.cohort_id = c.id";
 | 
			
		||||
        $sql .= " WHERE j.studyplan_id = :studyplan_id";
 | 
			
		||||
        $rs = $DB->get_recordset_sql($sql, ['studyplan_id' => $studyplan_id]);
 | 
			
		||||
        $cohorts = [];
 | 
			
		||||
        foreach($rs as $c)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($rs as $c) {
 | 
			
		||||
            $cohorts[] = self::make_cohort_model($c);
 | 
			
		||||
        }
 | 
			
		||||
        $rs->close();
 | 
			
		||||
| 
						 | 
				
			
			@ -404,18 +419,18 @@ class associationservice extends \external_api
 | 
			
		|||
        return new \external_multiple_structure(self::user_structure());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function all_associated($studyplan_id)
 | 
			
		||||
	{
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $users = [];
 | 
			
		||||
        // SQL JOIN script selecting all users that have a cohort linked to this studyplan 
 | 
			
		||||
        // or are directly linked
 | 
			
		||||
        // SQL JOIN script selecting all users that have a cohort linked to this studyplan .
 | 
			
		||||
        // or are directly linked.
 | 
			
		||||
        $sql = "SELECT DISTINCT u.id, u.username, u.firstname, u.lastname, u.idnumber, u.email
 | 
			
		||||
                FROM {user} u
 | 
			
		||||
                LEFT JOIN {cohort_members}              cm ON u.id = cm.userid
 | 
			
		||||
| 
						 | 
				
			
			@ -426,8 +441,7 @@ class associationservice extends \external_api
 | 
			
		|||
                ORDER BY u.lastname, u.firstname";
 | 
			
		||||
        $rs = $DB->get_recordset_sql($sql);
 | 
			
		||||
 | 
			
		||||
        foreach($rs as $u)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($rs as $u) {
 | 
			
		||||
            $users[] = self::make_user_model($u);
 | 
			
		||||
        }
 | 
			
		||||
        $rs->close();
 | 
			
		||||
| 
						 | 
				
			
			@ -436,15 +450,15 @@ class associationservice extends \external_api
 | 
			
		|||
        return $users;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function sortusermodels(&$list){
 | 
			
		||||
        return usort($list,function($a,$b){
 | 
			
		||||
    public static function sortusermodels(&$list) {
 | 
			
		||||
        return usort($list, function($a, $b) {
 | 
			
		||||
            $m= [];
 | 
			
		||||
            if(preg_match("/.*?([A-Z].*)/",$a['lastname'],$m)){
 | 
			
		||||
            if (preg_match("/.*?([A-Z].*)/", $a['lastname'], $m)) {
 | 
			
		||||
                $sort_ln_a = $m[1];
 | 
			
		||||
            } else {
 | 
			
		||||
                $sort_ln_a = $a['lastname'];
 | 
			
		||||
            }
 | 
			
		||||
            if(preg_match("/.*?([A-Z].*)/",$b['lastname'],$m)){
 | 
			
		||||
            if (preg_match("/.*?([A-Z].*)/", $b['lastname'], $m)) {
 | 
			
		||||
                $sort_ln_b = $m[1];
 | 
			
		||||
            } else {
 | 
			
		||||
                $sort_ln_b = $b['lastname'];
 | 
			
		||||
| 
						 | 
				
			
			@ -466,20 +480,20 @@ class associationservice extends \external_api
 | 
			
		|||
        return success::structure();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	// Actual functions
 | 
			
		||||
	// Actual functions.
 | 
			
		||||
	public static function cascade_cohortsync($studyplan_id)
 | 
			
		||||
	{
 | 
			
		||||
        $studyplan = studyplan::findById($studyplan_id);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        $enroller = new cascadecohortsync($studyplan);
 | 
			
		||||
        $enroller->sync();
 | 
			
		||||
 | 
			
		||||
        if(get_config("local_treestudyplan","csync_users")){
 | 
			
		||||
        if (get_config("local_treestudyplan", "csync_users")) {
 | 
			
		||||
            $userenroller = new cascadeusersync($studyplan);
 | 
			
		||||
            $userenroller->sync();
 | 
			
		||||
        }
 | 
			
		||||
        $studyplan->clear_csync_changed(); // Clear the csync required flag
 | 
			
		||||
        $studyplan->clear_csync_changed(); // Clear the csync required flag.
 | 
			
		||||
 | 
			
		||||
        return success::success()->model();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,30 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
class badgeinfo {
 | 
			
		||||
    private $badge; // Holds database record
 | 
			
		||||
    private $badge; // Holds database record.
 | 
			
		||||
 | 
			
		||||
    private const STATUSINFO = [
 | 
			
		||||
        BADGE_STATUS_INACTIVE => 'inactive',
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +38,7 @@ class badgeinfo {
 | 
			
		|||
        BADGE_STATUS_ACTIVE => 0,
 | 
			
		||||
        BADGE_STATUS_INACTIVE_LOCKED => 1,
 | 
			
		||||
        BADGE_STATUS_ACTIVE_LOCKED => 1,
 | 
			
		||||
        BADGE_STATUS_ARCHIVED => 1, // We don't want to edit archived badges anyway....     
 | 
			
		||||
        BADGE_STATUS_ARCHIVED => 1, // We don't want to edit archived badges anyway....     .
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    public function __construct(\core_badges\badge $badge) {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,43 +46,42 @@ class badgeinfo {
 | 
			
		|||
        $this->badge = $badge;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function name(){
 | 
			
		||||
    public function name() {
 | 
			
		||||
        return $this->badge->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function id_from_name($name){
 | 
			
		||||
    public static function id_from_name($name) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        return $DB->get_field("badge", "id", ['name' => $name]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function exists($id){
 | 
			
		||||
    public static function exists($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        return is_numeric($id) && $DB->record_exists('badge', array('id' => $id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of badge'),
 | 
			
		||||
            "infolink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'badge name'),
 | 
			
		||||
            "status" => new \external_value(PARAM_TEXT, 'badge status'),
 | 
			
		||||
            "locked" => new \external_value(PARAM_TEXT, 'badge lock status'),
 | 
			
		||||
            "criteria" => new \external_multiple_structure(new \external_value(PARAM_RAW, 'criteria text'),'badge criteria',VALUE_OPTIONAL),
 | 
			
		||||
            "criteria" => new \external_multiple_structure(new \external_value(PARAM_RAW, 'criteria text'), 'badge criteria', VALUE_OPTIONAL),
 | 
			
		||||
            "description"=> new \external_value(PARAM_TEXT, 'badge description'),
 | 
			
		||||
            "imageurl" => new \external_value(PARAM_TEXT, 'url of badge image'),
 | 
			
		||||
            "studentcount" => new \external_value(PARAM_INT, 'number of studyplan students that can get this badge',VALUE_OPTIONAL),
 | 
			
		||||
            "issuedcount" => new \external_value(PARAM_INT, 'number of studyplan students that have got this badge',VALUE_OPTIONAL),
 | 
			
		||||
        ],"Badge info",$value);
 | 
			
		||||
            "studentcount" => new \external_value(PARAM_INT, 'number of studyplan students that can get this badge', VALUE_OPTIONAL),
 | 
			
		||||
            "issuedcount" => new \external_value(PARAM_INT, 'number of studyplan students that have got this badge', VALUE_OPTIONAL),
 | 
			
		||||
        ], "Badge info", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(array $studentlist=null)
 | 
			
		||||
    {
 | 
			
		||||
    public function editor_model(array $studentlist=null) {
 | 
			
		||||
        $context = ($this->badge->type == BADGE_TYPE_SITE) ? \context_system::instance() : \context_course::instance($this->badge->courseid);
 | 
			
		||||
        // If the user is viewing another user's badge and doesn't have the right capability return only part of the data.
 | 
			
		||||
 | 
			
		||||
        $criteria = [];
 | 
			
		||||
        foreach($this->badge->get_criteria() as $bc){
 | 
			
		||||
        foreach ($this->badge->get_criteria() as $bc) {
 | 
			
		||||
            $criteria[] = $bc->get_title()." ".$bc->get_details();
 | 
			
		||||
        }
 | 
			
		||||
        $model = [
 | 
			
		||||
| 
						 | 
				
			
			@ -72,11 +92,11 @@ class badgeinfo {
 | 
			
		|||
            'locked' => self::LOCKEDINFO[$this->badge->status],
 | 
			
		||||
            'criteria' => $criteria,
 | 
			
		||||
            'description' => $this->badge->description,
 | 
			
		||||
            'imageurl' => \moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->badge->id, '/','f1')->out(false),
 | 
			
		||||
            'imageurl' => \moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->badge->id, '/', 'f1')->out(false),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // Add badge issue stats if a studentlist is attached to the request
 | 
			
		||||
        if(!empty($studentlist) && is_array($studentlist)){
 | 
			
		||||
        // Add badge issue stats if a studentlist is attached to the request.
 | 
			
		||||
        if (!empty($studentlist) && is_array($studentlist)) {
 | 
			
		||||
            $model['studentcount'] = count($studentlist);
 | 
			
		||||
            $model['issuedcount'] = $this->count_issued($studentlist);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -84,25 +104,23 @@ class badgeinfo {
 | 
			
		|||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED)
 | 
			
		||||
    {   
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of badge'),
 | 
			
		||||
            "infolink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'badge name'),
 | 
			
		||||
            "criteria" => new \external_multiple_structure(new \external_value(PARAM_RAW, 'criteria text'),'badge criteria',VALUE_OPTIONAL),
 | 
			
		||||
            "criteria" => new \external_multiple_structure(new \external_value(PARAM_RAW, 'criteria text'), 'badge criteria', VALUE_OPTIONAL),
 | 
			
		||||
            "description"=> new \external_value(PARAM_TEXT, 'badge description'),
 | 
			
		||||
            "imageurl" => new \external_value(PARAM_TEXT, 'url of badge image'),
 | 
			
		||||
            "issued" => new \external_value(PARAM_BOOL, 'badge is issued'),
 | 
			
		||||
            "dateissued" => new \external_value(PARAM_TEXT, 'date the badge was issued',VALUE_OPTIONAL),
 | 
			
		||||
            "dateexpire" => new \external_value(PARAM_TEXT, 'date the badge will expire',VALUE_OPTIONAL),
 | 
			
		||||
            "dateissued" => new \external_value(PARAM_TEXT, 'date the badge was issued', VALUE_OPTIONAL),
 | 
			
		||||
            "dateexpire" => new \external_value(PARAM_TEXT, 'date the badge will expire', VALUE_OPTIONAL),
 | 
			
		||||
            "uniquehash" => new \external_value(PARAM_TEXT, 'badge issue hash', VALUE_OPTIONAL),
 | 
			
		||||
            "issuedlink" => new \external_value(PARAM_TEXT, 'badge issue information link', VALUE_OPTIONAL),
 | 
			
		||||
        ],"Badge info",$value);
 | 
			
		||||
        ], "Badge info", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid)
 | 
			
		||||
    {
 | 
			
		||||
    public function user_model($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $context = ($this->badge->type == BADGE_TYPE_SITE) ? \context_system::instance() : \context_course::instance($this->badge->courseid);
 | 
			
		||||
| 
						 | 
				
			
			@ -110,24 +128,24 @@ class badgeinfo {
 | 
			
		|||
 | 
			
		||||
        // If the user is viewing another user's badge and doesn't have the right capability return only part of the data.
 | 
			
		||||
        $criteria = [];
 | 
			
		||||
        foreach($this->badge->get_criteria() as $bc){
 | 
			
		||||
        foreach ($this->badge->get_criteria() as $bc) {
 | 
			
		||||
            $criteria[] = $bc->get_title()."".$bc->get_details();
 | 
			
		||||
        }
 | 
			
		||||
        $badge = [
 | 
			
		||||
            'id' => $this->badge->id,
 | 
			
		||||
            'name' => $this->badge->name,
 | 
			
		||||
            'description' => $this->badge->description,
 | 
			
		||||
            'imageurl' => \moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->badge->id, '/','f1')->out(false),
 | 
			
		||||
            'imageurl' => \moodle_url::make_pluginfile_url($context->id, 'badges', 'badgeimage', $this->badge->id, '/', 'f1')->out(false),
 | 
			
		||||
            'criteria' => $criteria,
 | 
			
		||||
            'issued' => $issued,
 | 
			
		||||
            'infolink' => (new \moodle_url('/badges/overview.php', ['id' => $this->badge->id]))->out(false),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if($issued) {
 | 
			
		||||
        if ($issued) {
 | 
			
		||||
            $issueinfo = $DB->get_record('badge_issued', array('badgeid' => $this->badge->id, 'userid' => $userid));
 | 
			
		||||
            $badge['dateissued'] = date("Y-m-d",$issueinfo->dateissued);
 | 
			
		||||
            if($issueinfo->expiredate){
 | 
			
		||||
                $badge['dateexpire'] = date("Y-m-d",$issueinfo->dateexpire);
 | 
			
		||||
            $badge['dateissued'] = date("Y-m-d", $issueinfo->dateissued);
 | 
			
		||||
            if ($issueinfo->expiredate) {
 | 
			
		||||
                $badge['dateexpire'] = date("Y-m-d", $issueinfo->dateexpire);
 | 
			
		||||
            }
 | 
			
		||||
            $badge['uniquehash'] = $issueinfo->uniquehash;
 | 
			
		||||
            $badge['issuedlink'] =  (new \moodle_url('/badges/badge.php', ['hash' => $issueinfo->uniquehash]))->out(false);
 | 
			
		||||
| 
						 | 
				
			
			@ -136,11 +154,11 @@ class badgeinfo {
 | 
			
		|||
        return $badge;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function count_issued(array $student_ids){
 | 
			
		||||
    function count_issued(array $student_ids) {
 | 
			
		||||
        $issuecount = 0;
 | 
			
		||||
 | 
			
		||||
        foreach($student_ids as $userid){
 | 
			
		||||
            if($this->badge->is_issued($userid)){
 | 
			
		||||
        foreach ($student_ids as $userid) {
 | 
			
		||||
            if ($this->badge->is_issued($userid)) {
 | 
			
		||||
                $issuecount++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,15 +30,15 @@ class cascadecohortsync {
 | 
			
		|||
    private $studyplan;
 | 
			
		||||
    private $studyplanid;
 | 
			
		||||
 | 
			
		||||
    function __construct(studyplan $studyplan){
 | 
			
		||||
    function __construct(studyplan $studyplan) {
 | 
			
		||||
        $this->studyplan = $studyplan;
 | 
			
		||||
        $this->studyplanid = $studyplan->id();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static private function array_remove_value($array,$value){
 | 
			
		||||
    static private function array_remove_value($array, $value) {
 | 
			
		||||
        $a = [];
 | 
			
		||||
        foreach($array as $v){
 | 
			
		||||
            if($v != $value){
 | 
			
		||||
        foreach ($array as $v) {
 | 
			
		||||
            if ($v != $value) {
 | 
			
		||||
                $a[] = $v;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -25,7 +46,7 @@ class cascadecohortsync {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    static function uploadenrolmentmethods_get_group($courseid, $groupname) {
 | 
			
		||||
        // Function shamelessly copied from tool/uploadenrolmentmethods/locallib.php
 | 
			
		||||
        // Function shamelessly copied from tool/uploadenrolmentmethods/locallib.php.
 | 
			
		||||
        global $DB, $CFG;
 | 
			
		||||
 | 
			
		||||
        require_once($CFG->dirroot.'/group/lib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +65,7 @@ class cascadecohortsync {
 | 
			
		|||
        return $groupid;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sync(){
 | 
			
		||||
    function sync() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        /*  Explainer:
 | 
			
		||||
| 
						 | 
				
			
			@ -57,139 +78,139 @@ class cascadecohortsync {
 | 
			
		|||
        */
 | 
			
		||||
 | 
			
		||||
        $enrol = \enrol_get_plugin(self::METHOD);
 | 
			
		||||
        // Find the courses that need to be synced to the associated cohorts
 | 
			
		||||
        // Find the courses that need to be synced to the associated cohorts.
 | 
			
		||||
        $courseids = $this->studyplan->get_linked_course_ids();
 | 
			
		||||
        // And find the cohorts that are linked to this studyplan.
 | 
			
		||||
        $cohortids = $this->studyplan->get_linked_cohort_ids();
 | 
			
		||||
 | 
			
		||||
        // Next, for each course that is linked:
 | 
			
		||||
        foreach($courseids as $courseid){
 | 
			
		||||
        // Next, for each course that is linked:.
 | 
			
		||||
        foreach ($courseids as $courseid) {
 | 
			
		||||
            $course = \get_course($courseid);
 | 
			
		||||
            //\mtrace("Processing Course {$courseid} {$course->shortname}");
 | 
			
		||||
            // first create any nonexistent links
 | 
			
		||||
            foreach($cohortids as $cohortid){
 | 
			
		||||
                $cohort = $DB->get_record('cohort',['id'=>$cohortid]);
 | 
			
		||||
                //\mtrace("Processing cohort {$cohortid} {$cohort->shortname}");
 | 
			
		||||
            //\mtrace("Processing Course {$courseid} {$course->shortname}");.
 | 
			
		||||
            // first create any nonexistent links.
 | 
			
		||||
            foreach ($cohortids as $cohortid) {
 | 
			
		||||
                $cohort = $DB->get_record('cohort', ['id'=>$cohortid]);
 | 
			
		||||
                //\mtrace("Processing cohort {$cohortid} {$cohort->shortname}");.
 | 
			
		||||
 | 
			
		||||
                $instanceparams = [
 | 
			
		||||
                    'courseid' => $courseid,
 | 
			
		||||
                    'customint1' => $cohortid,
 | 
			
		||||
                    'enrol' => self::METHOD,
 | 
			
		||||
                    'roleid' => get_config("local_treestudyplan","csync_roleid"),
 | 
			
		||||
                    'roleid' => get_config("local_treestudyplan", "csync_roleid"),
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                $instancenewparams = [
 | 
			
		||||
                    'customint1' => $cohortid,
 | 
			
		||||
                    'enrol' => self::METHOD,
 | 
			
		||||
                    'roleid' => get_config("local_treestudyplan","csync_roleid"),
 | 
			
		||||
                    'roleid' => get_config("local_treestudyplan", "csync_roleid"),
 | 
			
		||||
                ];
 | 
			
		||||
 | 
			
		||||
                // Create group: 
 | 
			
		||||
                // Create group: .
 | 
			
		||||
 | 
			
		||||
                // 1:   check if a link exists
 | 
			
		||||
                //      If not, make it (maybe use some of the custom text to list the studyplans involved)
 | 
			
		||||
                // 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)) {
 | 
			
		||||
                    //\mtrace("Instance exists");
 | 
			
		||||
                    // it already exists
 | 
			
		||||
                    // check if this studyplan is already referenced in customtext4 in json format
 | 
			
		||||
                    //\mtrace("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 
 | 
			
		||||
                    //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")){
 | 
			
		||||
                    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 = [];
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if(!in_array($this->studyplanid ,$plans)){
 | 
			
		||||
                        // if not, add it to the reference
 | 
			
		||||
                        //\mtrace("Adding this plan to the list");
 | 
			
		||||
                    if (!in_array($this->studyplanid , $plans)) {
 | 
			
		||||
                        // if not, add it to the reference.
 | 
			
		||||
                        //\mtrace("Adding this plan to the list");.
 | 
			
		||||
                        $plans[] = (int)($this->studyplanid);
 | 
			
		||||
                        $enrol->update_instance($instance,(object)["customtext4"=>json_encode($plans)]);
 | 
			
		||||
                        $enrol->update_instance($instance, (object)["customtext4"=>json_encode($plans)]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                } else {
 | 
			
		||||
                    //\mtrace("New instance should be made");
 | 
			
		||||
                    //\mtrace("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
 | 
			
		||||
                    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'));
 | 
			
		||||
                        //\mtrace("Adding group {$groupname} for this method");
 | 
			
		||||
                        // and make sure the 
 | 
			
		||||
                        //\mtrace("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
 | 
			
		||||
                        //\mtrace("Instance ({$instanceid} created. Updateing instance with studyplan id");
 | 
			
		||||
                        // also record the (as of yet only) studyplans id requiring this association.
 | 
			
		||||
                        // in the customtext4 field in json format.
 | 
			
		||||
                        //\mtrace("Instance ({$instanceid} created. Updateing instance with studyplan id");.
 | 
			
		||||
                        $instance = $DB->get_record('enrol', array('id' => $instanceid));
 | 
			
		||||
                        $enrol->update_instance($instance,(object)["customtext4"=>json_encode([(int)($this->studyplanid)])]);
 | 
			
		||||
                        $enrol->update_instance($instance, (object)["customtext4"=>json_encode([(int)($this->studyplanid)])]);
 | 
			
		||||
 | 
			
		||||
                        //\mtrace("Synchronize the enrolment");
 | 
			
		||||
                        //\mtrace("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){
 | 
			
		||||
                            //\mtrace("Error during 'enrol_cohort_sync': code {$result}");
 | 
			
		||||
                        if ($result > 0) {
 | 
			
		||||
                            //\mtrace("Error during 'enrol_cohort_sync': code {$result}");.
 | 
			
		||||
                        }
 | 
			
		||||
                        $cohorttrace->finished();
 | 
			
		||||
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Instance not added for some reason, so report an error somewhere
 | 
			
		||||
                        // (or not)
 | 
			
		||||
                        //\mtrace("Error - instance not added for some reason");
 | 
			
		||||
                        // Instance not added for some reason, so report an error somewhere.
 | 
			
		||||
                        // (or not).
 | 
			
		||||
                        //\mtrace("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
 | 
			
		||||
            // 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 
 | 
			
		||||
            // 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
 | 
			
		||||
                //\mtrace("Autoremove scan for course {$courseid} {$course->shortname}");
 | 
			
		||||
                // find all cohort syncs for this course
 | 
			
		||||
            if (get_config("local_treestudyplan", "csync_autoremove")) {
 | 
			
		||||
                // Only try the autoremove if the option is enabled.
 | 
			
		||||
                //\mtrace("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"),
 | 
			
		||||
                    '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)){
 | 
			
		||||
                            //\mtrace("Found cohort sync instance that is not currently liked to the studyplan: {$instance->id}");
 | 
			
		||||
                            // So it may or may not need to be removed
 | 
			
		||||
                $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)) {
 | 
			
		||||
                            //\mtrace("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);
 | 
			
		||||
                            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)){
 | 
			
		||||
                                    //\mtrace("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);
 | 
			
		||||
                                    if(count($fplans) == 0){
 | 
			
		||||
                                        // delete the sync if there are no studyplan references left
 | 
			
		||||
                                        //\mtrace("No references are left, removing instance");
 | 
			
		||||
                            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)) {
 | 
			
		||||
                                    //\mtrace("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);
 | 
			
		||||
                                    if (count($fplans) == 0) {
 | 
			
		||||
                                        // delete the sync if there are no studyplan references left.
 | 
			
		||||
                                        //\mtrace("No references are left, removing instance");.
 | 
			
		||||
                                        $enrol->delete_instance($instance);
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        // otherwise just update the references so this studyplan is no longer linked
 | 
			
		||||
                                        //\mtrace("Still references left in the list, updating list...");
 | 
			
		||||
                                        $enrol->update_instance($instance,(object)["customtext4"=>json_encode($fplans)]);
 | 
			
		||||
                                        // otherwise just update the references so this studyplan is no longer linked.
 | 
			
		||||
                                        //\mtrace("Still references left in the list, updating list...");.
 | 
			
		||||
                                        $enrol->update_instance($instance, (object)["customtext4"=>json_encode($fplans)]);
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
| 
						 | 
				
			
			@ -198,6 +219,6 @@ class cascadecohortsync {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        //\mtrace("Cascading complete");
 | 
			
		||||
        //\mtrace("Cascading complete");.
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -8,11 +29,11 @@ class cascadeusersync {
 | 
			
		|||
    private const METHOD = "manual";
 | 
			
		||||
    private $studyplan;
 | 
			
		||||
 | 
			
		||||
    function __construct(studyplan $studyplan){
 | 
			
		||||
    function __construct(studyplan $studyplan) {
 | 
			
		||||
        $this->studyplan = $studyplan;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function sync(){
 | 
			
		||||
    function sync() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        /*  Explainer:
 | 
			
		||||
| 
						 | 
				
			
			@ -25,39 +46,39 @@ class cascadeusersync {
 | 
			
		|||
        */
 | 
			
		||||
 | 
			
		||||
        $enrol = \enrol_get_plugin(self::METHOD);
 | 
			
		||||
        // Find the courses that need to be synced to the associated cohorts
 | 
			
		||||
        // Find the courses that need to be synced to the associated cohorts.
 | 
			
		||||
        $courseids = $this->studyplan->get_linked_course_ids();
 | 
			
		||||
        // And find the users that are linked to this studyplan.
 | 
			
		||||
        $userids = $this->studyplan->get_linked_user_ids();
 | 
			
		||||
        // Get the roleid to use for synchronizations
 | 
			
		||||
        $roleid = get_config("local_treestudyplan","csync_roleid");
 | 
			
		||||
        // Get the roleid to use for synchronizations.
 | 
			
		||||
        $roleid = get_config("local_treestudyplan", "csync_roleid");
 | 
			
		||||
 | 
			
		||||
        // Next, for each course that is linked:
 | 
			
		||||
        foreach($courseids as $courseid){
 | 
			
		||||
        // Next, for each course that is linked:.
 | 
			
		||||
        foreach ($courseids as $courseid) {
 | 
			
		||||
            $course = \get_course($courseid);
 | 
			
		||||
            if(count($userids) > 0){
 | 
			
		||||
                // Get the manual enrol instance for this course
 | 
			
		||||
            if (count($userids) > 0) {
 | 
			
		||||
                // Get the manual enrol instance for this course.
 | 
			
		||||
                $instanceparams = ['courseid' => $courseid, 'enrol' => 'manual'];
 | 
			
		||||
                if(!($instance = $DB->get_record('enrol', $instanceparams))){
 | 
			
		||||
                if (!($instance = $DB->get_record('enrol', $instanceparams))) {
 | 
			
		||||
                    if ($instanceid = $enrol->add_default_instance($course)) {
 | 
			
		||||
                        $instance = $DB->get_record('enrol', array('id' => $instanceid));
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
                        // Instance not added for some reason, so report an error somewhere
 | 
			
		||||
                        // (or not)
 | 
			
		||||
                        // Instance not added for some reason, so report an error somewhere.
 | 
			
		||||
                        // (or not).
 | 
			
		||||
                        $instance = null;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if($instance !== null){
 | 
			
		||||
                    foreach($userids as $uid){
 | 
			
		||||
                if ($instance !== null) {
 | 
			
		||||
                    foreach ($userids as $uid) {
 | 
			
		||||
                        // Try a manual registration - it will just be updated if it is already there....
 | 
			
		||||
                        $enrol->enrol_user($instance,$uid,$roleid);
 | 
			
		||||
                        $enrol->enrol_user($instance, $uid, $roleid);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            // We do not do any autoremoval for user syncs, to avoid students losing access to the course data
 | 
			
		||||
            // We do not do any autoremoval for user syncs, to avoid students losing access to the course data.
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +43,7 @@ class completion {
 | 
			
		|||
    ];
 | 
			
		||||
 | 
			
		||||
    public static function label($completion) {
 | 
			
		||||
        if(array_key_exists($completion,self::LABELS)){
 | 
			
		||||
        if (array_key_exists($completion, self::LABELS)) {
 | 
			
		||||
            return self::LABELS[$completion];
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
| 
						 | 
				
			
			@ -32,20 +52,20 @@ class completion {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED){
 | 
			
		||||
        return new \external_value(PARAM_TEXT, 'completion state (failed|incomplete|pending|progress|completed|good|excellent)',$value);
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_value(PARAM_TEXT, 'completion state (failed|incomplete|pending|progress|completed|good|excellent)', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function count_states(array $states){
 | 
			
		||||
        // initialize result array
 | 
			
		||||
    public static function count_states(array $states) {
 | 
			
		||||
        // initialize result array.
 | 
			
		||||
        $statecount = [];
 | 
			
		||||
        foreach(array_keys(self::LABELS) as $key) {
 | 
			
		||||
        foreach (array_keys(self::LABELS) as $key) {
 | 
			
		||||
            $statecount[$key] = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // process all states in array and increment relevant counter for each one
 | 
			
		||||
        foreach($states as $c){
 | 
			
		||||
            if(array_key_exists($c,$statecount)){
 | 
			
		||||
        // process all states in array and increment relevant counter for each one.
 | 
			
		||||
        foreach ($states as $c) {
 | 
			
		||||
            if (array_key_exists($c, $statecount)) {
 | 
			
		||||
                $statecount[$c] += 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -15,19 +35,19 @@ class completionscanner
 | 
			
		|||
    private $gi = null;
 | 
			
		||||
    private $pending_cache = [];
 | 
			
		||||
 | 
			
		||||
    public static function supported($mod){
 | 
			
		||||
        if(!array_key_exists($mod,self::$mod_supported)){
 | 
			
		||||
    public static function supported($mod) {
 | 
			
		||||
        if (!array_key_exists($mod, self::$mod_supported)) {
 | 
			
		||||
            self::$mod_supported[$mod] = class_exists("\local_treestudyplan\\local\\ungradedscanners\\{$mod}_scanner");
 | 
			
		||||
        }
 | 
			
		||||
        return self::$mod_supported[$mod];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function get_course_students($courseid){
 | 
			
		||||
    public static function get_course_students($courseid) {
 | 
			
		||||
        global $CFG;
 | 
			
		||||
        if(!array_key_exists($courseid,self::$course_students)){
 | 
			
		||||
        if (!array_key_exists($courseid, self::$course_students)) {
 | 
			
		||||
            $students = [];
 | 
			
		||||
            $context = \context_course::instance($courseid);
 | 
			
		||||
            foreach (explode(',', $CFG->gradebookroles) as $roleid) {
 | 
			
		||||
            foreach (explode(', ', $CFG->gradebookroles) as $roleid) {
 | 
			
		||||
                $roleid = trim($roleid);
 | 
			
		||||
                $students = array_keys(get_role_users($roleid, $context, false, 'u.id', 'u.id ASC'));
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +56,7 @@ class completionscanner
 | 
			
		|||
        return self::$course_students[$courseid];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(\completion_criteria $crit,$course){
 | 
			
		||||
    public function __construct(\completion_criteria $crit, $course) {
 | 
			
		||||
        $this->courseid = $course->id;
 | 
			
		||||
        $this->course = $course;
 | 
			
		||||
        $this->modinfo = get_fast_modinfo($course);
 | 
			
		||||
| 
						 | 
				
			
			@ -44,20 +64,18 @@ class completionscanner
 | 
			
		|||
 | 
			
		||||
        $this->completioninfo = new \completion_info($course);
 | 
			
		||||
 | 
			
		||||
        // Find a related scanner if the type is an activity type
 | 
			
		||||
        if($crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY){
 | 
			
		||||
            // First find the course module
 | 
			
		||||
        // Find a related scanner if the type is an activity type.
 | 
			
		||||
        if ($crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
            // First find the course module.
 | 
			
		||||
            $this->cm = $this->modinfo->get_cm($crit->moduleinstance);
 | 
			
		||||
            $gi = grade_item::fetch(['itemtype' => 'mod', 'itemmodule' => $this->cm->modname, 'iteminstance' => $this->cm->instance, 'courseid' => $this->courseid]);
 | 
			
		||||
            if($gi !== false)
 | 
			
		||||
            {
 | 
			
		||||
                // Grade none items should not be relevant
 | 
			
		||||
                // Note that the grade status is probably only relevant if the item has not yet received a completion, but has been submitted
 | 
			
		||||
                if(($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE))
 | 
			
		||||
                {
 | 
			
		||||
                    // If it's a relevant grade type, initialize a scanner if possible
 | 
			
		||||
            if ($gi !== false) {
 | 
			
		||||
                // Grade none items should not be relevant.
 | 
			
		||||
                // Note that the grade status is probably only relevant if the item has not yet received a completion, but has been submitted.
 | 
			
		||||
                if (($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE)) {
 | 
			
		||||
                    // If it's a relevant grade type, initialize a scanner if possible.
 | 
			
		||||
                    $this->gi = $gi;
 | 
			
		||||
                    if(self::supported($gi->itemmodule)) {
 | 
			
		||||
                    if (self::supported($gi->itemmodule)) {
 | 
			
		||||
                        $scannerclass = "\local_treestudyplan\\local\ungradedscanners\\{$gi->itemmodule}_scanner";
 | 
			
		||||
                        $this->scanner = new $scannerclass($gi);
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -67,9 +85,9 @@ class completionscanner
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function pending($userid){
 | 
			
		||||
        if(!array_key_exists($userid, $this->pending_cache)){
 | 
			
		||||
            if($this->scanner === null) {
 | 
			
		||||
    public function pending($userid) {
 | 
			
		||||
        if (!array_key_exists($userid, $this->pending_cache)) {
 | 
			
		||||
            if ($this->scanner === null) {
 | 
			
		||||
                $this->pending_cache[$userid] = false;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -79,45 +97,45 @@ class completionscanner
 | 
			
		|||
        return $this->pending_cache[$userid];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure($value=VALUE_OPTIONAL){
 | 
			
		||||
    public static function structure($value=VALUE_OPTIONAL) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "ungraded" => new \external_value(PARAM_INT, 'number of ungraded submissions'),
 | 
			
		||||
            "completed" => new \external_value(PARAM_INT, 'number of completed students'),
 | 
			
		||||
            "completed_pass" => new \external_value(PARAM_INT, 'number of completed-pass students'),
 | 
			
		||||
            "completed_fail" => new \external_value(PARAM_INT, 'number of completed-fail students'),
 | 
			
		||||
            "students" => new \external_value(PARAM_INT, 'number of students that should submit'),
 | 
			
		||||
        ],"details about gradable submissions",$value);
 | 
			
		||||
        ], "details about gradable submissions", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function model(){
 | 
			
		||||
    public function model() {
 | 
			
		||||
 | 
			
		||||
        // get completion info
 | 
			
		||||
        // get completion info.
 | 
			
		||||
        $students = self::get_course_students($this->courseid);
 | 
			
		||||
        $completed = 0;
 | 
			
		||||
        $ungraded = 0;
 | 
			
		||||
        $completed_pass = 0;
 | 
			
		||||
        $completed_fail = 0;
 | 
			
		||||
        foreach($students as $userid){
 | 
			
		||||
            if($this->pending($userid)){
 | 
			
		||||
                // First check if the completion needs grading
 | 
			
		||||
        foreach ($students as $userid) {
 | 
			
		||||
            if ($this->pending($userid)) {
 | 
			
		||||
                // First check if the completion needs grading.
 | 
			
		||||
                $ungraded++;
 | 
			
		||||
            } else {
 | 
			
		||||
                $completion = $this->completioninfo->get_user_completion($userid,$this->crit);
 | 
			
		||||
                $completion = $this->completioninfo->get_user_completion($userid, $this->crit);
 | 
			
		||||
 | 
			
		||||
                if($this->crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY){
 | 
			
		||||
                    // If it's an activity completion, add all the relevant activities as sub-items
 | 
			
		||||
                    $completion_status = $this->completioninfo->get_grade_completion($this->cm,$userid);
 | 
			
		||||
                if ($this->crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                    // If it's an activity completion, add all the relevant activities as sub-items.
 | 
			
		||||
                    $completion_status = $this->completioninfo->get_grade_completion($this->cm, $userid);
 | 
			
		||||
 | 
			
		||||
                    if($completion_status == COMPLETION_COMPLETE_PASS){
 | 
			
		||||
                    if ($completion_status == COMPLETION_COMPLETE_PASS) {
 | 
			
		||||
                        $completed_pass++;
 | 
			
		||||
                    } else if ($completion_status == COMPLETION_COMPLETE_FAIL){
 | 
			
		||||
                    } else if ($completion_status == COMPLETION_COMPLETE_FAIL) {
 | 
			
		||||
                        $completed_fail++;
 | 
			
		||||
                    } else if ($completion_status == COMPLETION_COMPLETE){
 | 
			
		||||
                    } else if ($completion_status == COMPLETION_COMPLETE) {
 | 
			
		||||
                        $completed++;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    if($completion->is_complete()){
 | 
			
		||||
                    if ($completion->is_complete()) {
 | 
			
		||||
                        $completed++;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,35 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class contextinfo {
 | 
			
		||||
    public $context;
 | 
			
		||||
    public function __construct($context){
 | 
			
		||||
    public function __construct($context) {
 | 
			
		||||
        $this->context = $context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'context name'),
 | 
			
		||||
            "shortname" => new \external_value(PARAM_TEXT, 'context short name'),
 | 
			
		||||
| 
						 | 
				
			
			@ -21,14 +41,14 @@ class contextinfo {
 | 
			
		|||
    public function model() {
 | 
			
		||||
 | 
			
		||||
        $ctxPath = array_reverse($this->context->get_parent_context_ids(true));
 | 
			
		||||
        if(count($ctxPath) > 1 && $ctxPath[0] == 1) {
 | 
			
		||||
        if (count($ctxPath) > 1 && $ctxPath[0] == 1) {
 | 
			
		||||
            array_shift($ctxPath);
 | 
			
		||||
        }
 | 
			
		||||
        return  [
 | 
			
		||||
            "name" => $this->context->get_context_name(false,false),
 | 
			
		||||
            "shortname" => $this->context->get_context_name(false,true),
 | 
			
		||||
            "path" => array_map(function($c){ return \context::instance_by_id($c)->get_context_name(false,false);},$ctxPath),
 | 
			
		||||
            "shortpath" => array_map(function($c){ return \context::instance_by_id($c)->get_context_name(false,true);},$ctxPath),
 | 
			
		||||
            "name" => $this->context->get_context_name(false, false),
 | 
			
		||||
            "shortname" => $this->context->get_context_name(false, true),
 | 
			
		||||
            "path" => array_map(function($c) { return \context::instance_by_id($c)->get_context_name(false, false);}, $ctxPath),
 | 
			
		||||
            "shortpath" => array_map(function($c) { return \context::instance_by_id($c)->get_context_name(false, true);}, $ctxPath),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +57,7 @@ class contextinfo {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    public static function context_by_id($contextid): \context {
 | 
			
		||||
        if($contextid <= 1){
 | 
			
		||||
        if ($contextid <= 1) {
 | 
			
		||||
            $contextid = 1;
 | 
			
		||||
        }
 | 
			
		||||
        return \context::instance_by_id($contextid);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -17,19 +37,19 @@ class corecompletioninfo {
 | 
			
		|||
    private $modinfo;
 | 
			
		||||
    private static $COMPLETION_HANDLES = null;
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->course->id;
 | 
			
		||||
    }
 | 
			
		||||
    public function __construct($course){
 | 
			
		||||
    public function __construct($course) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->course = $course;
 | 
			
		||||
        $this->completion = new \completion_info($this->course);
 | 
			
		||||
        $this->modinfo = get_fast_modinfo($this->course);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static public function completiontypes(){
 | 
			
		||||
    static public function completiontypes() {
 | 
			
		||||
        global $COMPLETION_CRITERIA_TYPES;
 | 
			
		||||
        // Just return the keys of the global array COMPLETION_CRITERIA_TYPES, so we don't have to manually
 | 
			
		||||
        // Just return the keys of the global array COMPLETION_CRITERIA_TYPES, so we don't have to manually.
 | 
			
		||||
        // add any completion types....
 | 
			
		||||
        return \array_keys($COMPLETION_CRITERIA_TYPES);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -38,99 +58,99 @@ class corecompletioninfo {
 | 
			
		|||
     * Translate a numeric completion constant to a text string
 | 
			
		||||
     * @param $completion The completion code as defined in completionlib.php to translate to a text handle
 | 
			
		||||
     */
 | 
			
		||||
    static public function completion_handle($completion){
 | 
			
		||||
        if(empty(self::$COMPLETION_HANDLES)){
 | 
			
		||||
            // Cache the translation table, to avoid overhead
 | 
			
		||||
    static public function completion_handle($completion) {
 | 
			
		||||
        if (empty(self::$COMPLETION_HANDLES)) {
 | 
			
		||||
            // Cache the translation table, to avoid overhead.
 | 
			
		||||
            self::$COMPLETION_HANDLES = [
 | 
			
		||||
                COMPLETION_INCOMPLETE => "incomplete",
 | 
			
		||||
                COMPLETION_COMPLETE => "complete",
 | 
			
		||||
                COMPLETION_COMPLETE_PASS => "complete-pass",
 | 
			
		||||
                COMPLETION_COMPLETE_FAIL => "complete-fail",
 | 
			
		||||
                COMPLETION_COMPLETE_FAIL_HIDDEN => "complete-fail"]; // the front end won't differentiate between hidden or not
 | 
			
		||||
                COMPLETION_COMPLETE_FAIL_HIDDEN => "complete-fail"]; // the front end won't differentiate between hidden or not.
 | 
			
		||||
        }
 | 
			
		||||
        return self::$COMPLETION_HANDLES[$completion] ?? "undefined";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function completion_item_editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function completion_item_editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT,'criteria id',VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT,'name of subitem',VALUE_OPTIONAL),
 | 
			
		||||
            "link" => new \external_value(PARAM_TEXT, 'optional link to more details',VALUE_OPTIONAL),
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'criteria id', VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT, 'name of subitem', VALUE_OPTIONAL),
 | 
			
		||||
            "link" => new \external_value(PARAM_TEXT, 'optional link to more details', VALUE_OPTIONAL),
 | 
			
		||||
            "details" => new \external_single_structure([
 | 
			
		||||
                "type" => new \external_value(PARAM_RAW, 'type',VALUE_OPTIONAL),
 | 
			
		||||
                "criteria" => new \external_value(PARAM_RAW, 'criteria',VALUE_OPTIONAL),
 | 
			
		||||
                "requirement" => new \external_value(PARAM_RAW, 'requirement',VALUE_OPTIONAL),
 | 
			
		||||
                "status" => new \external_value(PARAM_RAW, 'status',VALUE_OPTIONAL),
 | 
			
		||||
                "type" => new \external_value(PARAM_RAW, 'type', VALUE_OPTIONAL),
 | 
			
		||||
                "criteria" => new \external_value(PARAM_RAW, 'criteria', VALUE_OPTIONAL),
 | 
			
		||||
                "requirement" => new \external_value(PARAM_RAW, 'requirement', VALUE_OPTIONAL),
 | 
			
		||||
                "status" => new \external_value(PARAM_RAW, 'status', VALUE_OPTIONAL),
 | 
			
		||||
            ]),
 | 
			
		||||
            "progress" => completionscanner::structure(),
 | 
			
		||||
         ], 'completion type',$value);
 | 
			
		||||
         ], 'completion type', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function completion_type_editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function completion_type_editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "items" => new \external_multiple_structure(self::completion_item_editor_structure(),'subitems',VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT,'optional title',VALUE_OPTIONAL),
 | 
			
		||||
            "desc" => new \external_value(PARAM_TEXT, 'optional description',VALUE_OPTIONAL),
 | 
			
		||||
            "items" => new \external_multiple_structure(self::completion_item_editor_structure(), 'subitems', VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT, 'optional title', VALUE_OPTIONAL),
 | 
			
		||||
            "desc" => new \external_value(PARAM_TEXT, 'optional description', VALUE_OPTIONAL),
 | 
			
		||||
            "type" => new \external_value(PARAM_TEXT, 'completion type name'),
 | 
			
		||||
            "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation for this type ["all","any"]'),
 | 
			
		||||
         ], 'completion type',$value);
 | 
			
		||||
            "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation for this type ["all", "any"]'),
 | 
			
		||||
         ], 'completion type', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
           "conditions" => new \external_multiple_structure(self::completion_type_editor_structure(),'completion conditions'),
 | 
			
		||||
           "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation ["all","any"]'),
 | 
			
		||||
           "enabled" => new \external_value(PARAM_BOOL,"whether completion is enabled here"),
 | 
			
		||||
        ], 'course completion info',$value);
 | 
			
		||||
           "conditions" => new \external_multiple_structure(self::completion_type_editor_structure(), 'completion conditions'),
 | 
			
		||||
           "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation ["all", "any"]'),
 | 
			
		||||
           "enabled" => new \external_value(PARAM_BOOL, "whether completion is enabled here"),
 | 
			
		||||
        ], 'course completion info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function completion_item_user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function completion_item_user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT,'id of completion',VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT,'name of subitem',VALUE_OPTIONAL),
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of completion', VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT, 'name of subitem', VALUE_OPTIONAL),
 | 
			
		||||
            "details" => new \external_single_structure([
 | 
			
		||||
                "type" => new \external_value(PARAM_RAW, 'type',VALUE_OPTIONAL),
 | 
			
		||||
                "criteria" => new \external_value(PARAM_RAW, 'criteria',VALUE_OPTIONAL),
 | 
			
		||||
                "requirement" => new \external_value(PARAM_RAW, 'requirement',VALUE_OPTIONAL),
 | 
			
		||||
                "status" => new \external_value(PARAM_RAW, 'status',VALUE_OPTIONAL),
 | 
			
		||||
                "type" => new \external_value(PARAM_RAW, 'type', VALUE_OPTIONAL),
 | 
			
		||||
                "criteria" => new \external_value(PARAM_RAW, 'criteria', VALUE_OPTIONAL),
 | 
			
		||||
                "requirement" => new \external_value(PARAM_RAW, 'requirement', VALUE_OPTIONAL),
 | 
			
		||||
                "status" => new \external_value(PARAM_RAW, 'status', VALUE_OPTIONAL),
 | 
			
		||||
            ]),
 | 
			
		||||
            "link" => new \external_value(PARAM_TEXT, 'optional link to more details',VALUE_OPTIONAL),
 | 
			
		||||
            "link" => new \external_value(PARAM_TEXT, 'optional link to more details', VALUE_OPTIONAL),
 | 
			
		||||
            "completed" => new \external_value(PARAM_BOOL, 'simple completed or not'),
 | 
			
		||||
            "status" => new \external_value(PARAM_TEXT, 'extended completion status ["incomplete","progress","complete", "complete-pass","complete-fail"]'),
 | 
			
		||||
            "pending" => new \external_value(PARAM_BOOL, 'optional pending state, for submitted but not yet reviewed activities',VALUE_OPTIONAL),
 | 
			
		||||
            "grade" => new \external_value(PARAM_TEXT, 'optional grade result for this subitem',VALUE_OPTIONAL),
 | 
			
		||||
            "feedback" => new \external_value(PARAM_RAW, 'optional feedback for this subitem ',VALUE_OPTIONAL),
 | 
			
		||||
         ], 'completion type',$value);
 | 
			
		||||
            "status" => new \external_value(PARAM_TEXT, 'extended completion status ["incomplete", "progress", "complete", "complete-pass", "complete-fail"]'),
 | 
			
		||||
            "pending" => new \external_value(PARAM_BOOL, 'optional pending state, for submitted but not yet reviewed activities', VALUE_OPTIONAL),
 | 
			
		||||
            "grade" => new \external_value(PARAM_TEXT, 'optional grade result for this subitem', VALUE_OPTIONAL),
 | 
			
		||||
            "feedback" => new \external_value(PARAM_RAW, 'optional feedback for this subitem ', VALUE_OPTIONAL),
 | 
			
		||||
         ], 'completion type', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function completion_type_user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function completion_type_user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "items" => new \external_multiple_structure(self::completion_item_user_structure(),'subitems',VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT,'optional title',VALUE_OPTIONAL),
 | 
			
		||||
            "desc" => new \external_value(PARAM_TEXT, 'optional description',VALUE_OPTIONAL),
 | 
			
		||||
            "items" => new \external_multiple_structure(self::completion_item_user_structure(), 'subitems', VALUE_OPTIONAL),
 | 
			
		||||
            "title" => new \external_value(PARAM_TEXT, 'optional title', VALUE_OPTIONAL),
 | 
			
		||||
            "desc" => new \external_value(PARAM_TEXT, 'optional description', VALUE_OPTIONAL),
 | 
			
		||||
            "type" => new \external_value(PARAM_TEXT, 'completion type name'),
 | 
			
		||||
            "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation for this type ["all","any"]'),
 | 
			
		||||
            "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation for this type ["all", "any"]'),
 | 
			
		||||
            "completed" => new \external_value(PARAM_BOOL, 'current completion value for this type'),
 | 
			
		||||
            "status" => new \external_value(PARAM_TEXT, 'extended completion status ["incomplete","progress","complete", "complete-pass","complete-fail"]'),
 | 
			
		||||
            "status" => new \external_value(PARAM_TEXT, 'extended completion status ["incomplete", "progress", "complete", "complete-pass", "complete-fail"]'),
 | 
			
		||||
            "progress" => new \external_value(PARAM_INT, 'completed sub-conditions'),
 | 
			
		||||
            "count" => new \external_value(PARAM_INT, 'total number of sub-conditions'),
 | 
			
		||||
         ], 'completion type',$value);
 | 
			
		||||
         ], 'completion type', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "progress" => new \external_value(PARAM_INT, 'completed sub-conditions'),
 | 
			
		||||
            "enabled" => new \external_value(PARAM_BOOL,"whether completion is enabled here"),
 | 
			
		||||
            "tracked" => new \external_value(PARAM_BOOL,"whether completion is tracked for the user",VALUE_OPTIONAL),
 | 
			
		||||
            "enabled" => new \external_value(PARAM_BOOL, "whether completion is enabled here"),
 | 
			
		||||
            "tracked" => new \external_value(PARAM_BOOL, "whether completion is tracked for the user", VALUE_OPTIONAL),
 | 
			
		||||
            "count" => new \external_value(PARAM_INT, 'total number of sub-conditions'),
 | 
			
		||||
            "conditions" => new \external_multiple_structure(self::completion_type_user_structure(),'completion conditions'),
 | 
			
		||||
            "conditions" => new \external_multiple_structure(self::completion_type_user_structure(), 'completion conditions'),
 | 
			
		||||
            "completed" => new \external_value(PARAM_BOOL, 'current completion value'),
 | 
			
		||||
            "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation ["all","any"]'),
 | 
			
		||||
            "pending" => new \external_value(PARAM_BOOL,"true if the user has any assignments pending grading",VALUE_OPTIONAL),
 | 
			
		||||
        ], 'course completion info',$value);
 | 
			
		||||
            "aggregation" => new \external_value(PARAM_TEXT, 'completion aggregation ["all", "any"]'),
 | 
			
		||||
            "pending" => new \external_value(PARAM_BOOL, "true if the user has any assignments pending grading", VALUE_OPTIONAL),
 | 
			
		||||
        ], 'course completion info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function aggregation_handle($method){
 | 
			
		||||
    private static function aggregation_handle($method) {
 | 
			
		||||
        return ($method==COMPLETION_AGGREGATION_ALL)?"all":"any";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,21 +158,20 @@ class corecompletioninfo {
 | 
			
		|||
        global $DB, $CFG, $COMPLETION_CRITERIA_TYPES;
 | 
			
		||||
 | 
			
		||||
        $conditions = [];
 | 
			
		||||
        $aggregation = "all"; // default
 | 
			
		||||
        $aggregation = "all"; // default.
 | 
			
		||||
        $info = [
 | 
			
		||||
            "conditions" => $conditions,
 | 
			
		||||
            "aggregation" => self::aggregation_handle($this->completion->get_aggregation_method()),
 | 
			
		||||
            "enabled" => $this->completion->is_enabled()
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // Check if completion tracking is enabled for this course - otherwise, revert to defaults 
 | 
			
		||||
        if($this->completion->is_enabled())
 | 
			
		||||
        {
 | 
			
		||||
        // Check if completion tracking is enabled for this course - otherwise, revert to defaults .
 | 
			
		||||
        if ($this->completion->is_enabled()) {
 | 
			
		||||
            $aggregation = $this->completion->get_aggregation_method();
 | 
			
		||||
            // Loop through all condition types to see if they are applicable
 | 
			
		||||
            foreach(self::completiontypes() as $type){
 | 
			
		||||
                $criterias = $this->completion->get_criteria($type); // Returns array of relevant criteria items
 | 
			
		||||
                if(count($criterias) > 0 ) // Only take it into account if the criteria count is > 0
 | 
			
		||||
            // Loop through all condition types to see if they are applicable.
 | 
			
		||||
            foreach (self::completiontypes() as $type) {
 | 
			
		||||
                $criterias = $this->completion->get_criteria($type); // Returns array of relevant criteria items.
 | 
			
		||||
                if (count($criterias) > 0 ) // Only take it into account if the criteria count is > 0.
 | 
			
		||||
                {
 | 
			
		||||
                    $cinfo = [
 | 
			
		||||
                        "type" => $COMPLETION_CRITERIA_TYPES[$type],
 | 
			
		||||
| 
						 | 
				
			
			@ -161,13 +180,13 @@ class corecompletioninfo {
 | 
			
		|||
                        "items" => [],
 | 
			
		||||
                    ];
 | 
			
		||||
 | 
			
		||||
                    foreach($criterias as $criteria){
 | 
			
		||||
                        // Unfortunately, we cannot easily get the criteria details with get_details() without having a 
 | 
			
		||||
                        // user completion object involved, so'we'll have to retrieve the details per completion type
 | 
			
		||||
                        // See moodle/completion/criteria/completion_criteria_*.php::get_details() for the code that is
 | 
			
		||||
                        // in the code below is based on
 | 
			
		||||
                    foreach ($criterias as $criteria) {
 | 
			
		||||
                        // Unfortunately, we cannot easily get the criteria details with get_details() without having a .
 | 
			
		||||
                        // user completion object involved, so'we'll have to retrieve the details per completion type.
 | 
			
		||||
                        // See moodle/completion/criteria/completion_criteria_*.php::get_details() for the code that is.
 | 
			
		||||
                        // in the code below is based on.
 | 
			
		||||
 | 
			
		||||
                        if($type == COMPLETION_CRITERIA_TYPE_SELF){
 | 
			
		||||
                        if ($type == COMPLETION_CRITERIA_TYPE_SELF) {
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => $criteria->get_title(),
 | 
			
		||||
                                "criteria" => $criteria->get_title(),
 | 
			
		||||
| 
						 | 
				
			
			@ -175,15 +194,15 @@ class corecompletioninfo {
 | 
			
		|||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_DATE){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_DATE) {
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => get_string('datepassed', 'completion'),
 | 
			
		||||
                                "criteria" => get_string('remainingenroleduntildate', 'completion'),
 | 
			
		||||
                                "requirement" => date("Y-m-d",$criteria->timeend),
 | 
			
		||||
                                "requirement" => date("Y-m-d", $criteria->timeend),
 | 
			
		||||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_UNENROL){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_UNENROL) {
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => get_string('unenrolment', 'completion'),
 | 
			
		||||
                                "criteria" => get_string('unenrolment', 'completion'),
 | 
			
		||||
| 
						 | 
				
			
			@ -191,12 +210,12 @@ class corecompletioninfo {
 | 
			
		|||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_ACTIVITY){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                            $cm = $this->modinfo->get_cm($criteria->moduleinstance);
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => $criteria->get_title(),
 | 
			
		||||
                                "criteria" => "", // Will be built in a moment by code copied from completion_criteria_activity.php
 | 
			
		||||
                                "requirement" => "", // Will be built momentarily by code copied from completion_criteria_activity.php
 | 
			
		||||
                                "criteria" => "", // Will be built in a moment by code copied from completion_criteria_activity.php.
 | 
			
		||||
                                "requirement" => "", // Will be built momentarily by code copied from completion_criteria_activity.php.
 | 
			
		||||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                            if ($cm->has_view()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -204,12 +223,12 @@ class corecompletioninfo {
 | 
			
		|||
                            } else {
 | 
			
		||||
                                $details['criteria'] = $cm->get_formatted_name();
 | 
			
		||||
                            }
 | 
			
		||||
                            // Build requirements
 | 
			
		||||
                            // Build requirements.
 | 
			
		||||
                            $details['requirement'] = array();
 | 
			
		||||
 | 
			
		||||
                            if ($cm->completion == COMPLETION_TRACKING_MANUAL) {
 | 
			
		||||
                                $details['requirement'][] = get_string('markingyourselfcomplete', 'completion');
 | 
			
		||||
                            } elseif ($cm->completion == COMPLETION_TRACKING_AUTOMATIC) {
 | 
			
		||||
                            } else if ($cm->completion == COMPLETION_TRACKING_AUTOMATIC) {
 | 
			
		||||
                                if ($cm->completionview) {
 | 
			
		||||
                                    $modulename = \core_text::strtolower(get_string('modulename', $criteria->module));
 | 
			
		||||
                                    $details['requirement'][] = get_string('viewingactivity', 'completion', $modulename);
 | 
			
		||||
| 
						 | 
				
			
			@ -227,7 +246,7 @@ class corecompletioninfo {
 | 
			
		|||
                            $details['requirement'] = implode(', ', $details['requirement']);
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_DURATION){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_DURATION) {
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => get_string('periodpostenrolment', 'completion'),
 | 
			
		||||
                                "criteria" => get_string('remainingenroledfortime', 'completion'),
 | 
			
		||||
| 
						 | 
				
			
			@ -235,16 +254,16 @@ class corecompletioninfo {
 | 
			
		|||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_GRADE){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_GRADE) {
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => get_string('coursegrade', 'completion'),
 | 
			
		||||
                                "criteria" => get_string('graderequired', 'completion'),
 | 
			
		||||
                                // TODO: convert to selected representation (letter, percentage, etc)
 | 
			
		||||
                                // TODO: convert to selected representation (letter, percentage, etc).
 | 
			
		||||
                                "requirement" => get_string('graderequired', 'completion').": ".format_float($criteria->gradepass, 1),
 | 
			
		||||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_ROLE){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_ROLE) {
 | 
			
		||||
                            $criteria = $criteria->get_title();
 | 
			
		||||
 | 
			
		||||
                            $details = [
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +273,7 @@ class corecompletioninfo {
 | 
			
		|||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_COURSE){
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_COURSE) {
 | 
			
		||||
                            $prereq = get_course($criteria->courseinstance);
 | 
			
		||||
                            $coursecontext = \context_course::instance($prereq->id, MUST_EXIST);
 | 
			
		||||
                            $fullname = format_string($prereq->fullname, true, array('context' => $coursecontext));
 | 
			
		||||
| 
						 | 
				
			
			@ -265,7 +284,7 @@ class corecompletioninfo {
 | 
			
		|||
                                "status" => "",
 | 
			
		||||
                            ];
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // Moodle added a criteria type
 | 
			
		||||
                            // Moodle added a criteria type.
 | 
			
		||||
                            $details = [
 | 
			
		||||
                                "type" => "",
 | 
			
		||||
                                "criteria" => "",
 | 
			
		||||
| 
						 | 
				
			
			@ -274,7 +293,7 @@ class corecompletioninfo {
 | 
			
		|||
                            ];
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        $scanner = new completionscanner($criteria,$this->course);
 | 
			
		||||
                        $scanner = new completionscanner($criteria, $this->course);
 | 
			
		||||
 | 
			
		||||
                        // only add the items list if we actually have items...
 | 
			
		||||
                        $cinfo["items"][] = [
 | 
			
		||||
| 
						 | 
				
			
			@ -296,18 +315,18 @@ class corecompletioninfo {
 | 
			
		|||
        return $info;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function aggregate_completions($typeaggregation,$completions){
 | 
			
		||||
    private function aggregate_completions($typeaggregation, $completions) {
 | 
			
		||||
        $completed = 0;
 | 
			
		||||
        $count = count($completions);
 | 
			
		||||
        foreach($completions as $c){
 | 
			
		||||
            if($c->is_complete()){
 | 
			
		||||
        foreach ($completions as $c) {
 | 
			
		||||
            if ($c->is_complete()) {
 | 
			
		||||
                $completed++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if($typeaggregation == COMPLETION_AGGREGATION_ALL){
 | 
			
		||||
        if ($typeaggregation == COMPLETION_AGGREGATION_ALL) {
 | 
			
		||||
            return $completed >= $count;
 | 
			
		||||
        }
 | 
			
		||||
        else { // COMPLETION_AGGREGATION_ANY
 | 
			
		||||
        else { // COMPLETION_AGGREGATION_ANY.
 | 
			
		||||
            return $completed > 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -327,17 +346,16 @@ class corecompletioninfo {
 | 
			
		|||
            "tracked" => $this->completion->is_tracked_user($userid),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // Check if completion tracking is enabled for this course - otherwise, revert to defaults 
 | 
			
		||||
        if($this->completion->is_enabled() && $this->completion->is_tracked_user($userid))
 | 
			
		||||
        {
 | 
			
		||||
        // Check if completion tracking is enabled for this course - otherwise, revert to defaults .
 | 
			
		||||
        if ($this->completion->is_enabled() && $this->completion->is_tracked_user($userid)) {
 | 
			
		||||
            $anypending = false;
 | 
			
		||||
            // Loop through all conditions to see if they are applicable
 | 
			
		||||
            foreach(self::completiontypes() as $type){
 | 
			
		||||
                // Get the main completion for this type
 | 
			
		||||
                $completions = $this->completion->get_completions($userid,$type);
 | 
			
		||||
                if(count($completions) > 0){
 | 
			
		||||
            // Loop through all conditions to see if they are applicable.
 | 
			
		||||
            foreach (self::completiontypes() as $type) {
 | 
			
		||||
                // Get the main completion for this type.
 | 
			
		||||
                $completions = $this->completion->get_completions($userid, $type);
 | 
			
		||||
                if (count($completions) > 0) {
 | 
			
		||||
                    $typeaggregation = $this->completion->get_aggregation_method($type);
 | 
			
		||||
                    $completed = $this->aggregate_completions($typeaggregation,$completions);
 | 
			
		||||
                    $completed = $this->aggregate_completions($typeaggregation, $completions);
 | 
			
		||||
                    $cinfo = [
 | 
			
		||||
                        "type" => $COMPLETION_CRITERIA_TYPES[$type],
 | 
			
		||||
                        "aggregation" => self::aggregation_handle($typeaggregation),
 | 
			
		||||
| 
						 | 
				
			
			@ -348,67 +366,67 @@ class corecompletioninfo {
 | 
			
		|||
                    ];
 | 
			
		||||
 | 
			
		||||
                    $progress = 0;
 | 
			
		||||
                    foreach($completions as $completion){
 | 
			
		||||
                    foreach ($completions as $completion) {
 | 
			
		||||
                        $criteria = $completion->get_criteria();
 | 
			
		||||
 | 
			
		||||
                        if($completion->is_complete()) {
 | 
			
		||||
                            $progress += 1; // Add a point to the progress counter
 | 
			
		||||
                        if ($completion->is_complete()) {
 | 
			
		||||
                            $progress += 1; // Add a point to the progress counter.
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        $iinfo = [
 | 
			
		||||
                            "id" => $criteria->id,
 | 
			
		||||
                            "title" => $criteria->get_title_detailed(),
 | 
			
		||||
                            "details" => $criteria->get_details($completion),
 | 
			
		||||
                            "completed" => $completion->is_complete(), // Make sure to override for activi
 | 
			
		||||
                            "completed" => $completion->is_complete(), // Make sure to override for activi.
 | 
			
		||||
                            "status" => self::completion_handle($completion->is_complete()?COMPLETION_COMPLETE:COMPLETION_INCOMPLETE),
 | 
			
		||||
                        ];
 | 
			
		||||
 | 
			
		||||
                        if($type == COMPLETION_CRITERIA_TYPE_ACTIVITY){
 | 
			
		||||
                        if ($type == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                            $cm = $this->modinfo->get_cm($criteria->moduleinstance);
 | 
			
		||||
                            // If it's an activity completion, add all the relevant activities as sub-items
 | 
			
		||||
                            $completion_status = $this->completion->get_grade_completion($cm,$userid);
 | 
			
		||||
                            // If it's an activity completion, add all the relevant activities as sub-items.
 | 
			
		||||
                            $completion_status = $this->completion->get_grade_completion($cm, $userid);
 | 
			
		||||
                            $iinfo['status'] = self::completion_handle($completion_status);
 | 
			
		||||
                            // Re-evaluate the completed value, to make sure COMPLETE_FAIL doesn't creep in as completed
 | 
			
		||||
                            $iinfo['completed'] = in_array($completion_status,[COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS]);
 | 
			
		||||
                            // Determine the grade (retrieve from grade item, not from completion)
 | 
			
		||||
                            $grade = $this->get_grade($cm,$userid);
 | 
			
		||||
                            // Re-evaluate the completed value, to make sure COMPLETE_FAIL doesn't creep in as completed.
 | 
			
		||||
                            $iinfo['completed'] = in_array($completion_status, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS]);
 | 
			
		||||
                            // Determine the grade (retrieve from grade item, not from completion).
 | 
			
		||||
                            $grade = $this->get_grade($cm, $userid);
 | 
			
		||||
                            $iinfo['grade'] = $grade->grade;
 | 
			
		||||
                            $iinfo['feedback'] = $grade->feedback;
 | 
			
		||||
                            $iinfo['pending'] = $grade->pending;
 | 
			
		||||
 | 
			
		||||
                            $anypending = $anypending || $grade->pending;
 | 
			
		||||
                            // Overwrite the status with progress if something has been graded, or is pending
 | 
			
		||||
                            if($completion_status != COMPLETION_INCOMPLETE || $anypending){
 | 
			
		||||
                                if($cinfo["status"] == "incomplete"){
 | 
			
		||||
                            // Overwrite the status with progress if something has been graded, or is pending.
 | 
			
		||||
                            if ($completion_status != COMPLETION_INCOMPLETE || $anypending) {
 | 
			
		||||
                                if ($cinfo["status"] == "incomplete") {
 | 
			
		||||
                                    $cinfo["status"] = "progress";
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                        }
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_GRADE){
 | 
			
		||||
                            // Make sure we provide the current course grade
 | 
			
		||||
                        else if ($type == COMPLETION_CRITERIA_TYPE_GRADE) {
 | 
			
		||||
                            // Make sure we provide the current course grade.
 | 
			
		||||
                            $iinfo['grade'] = floatval($iinfo['details']['status']);
 | 
			
		||||
                            if($iinfo["grade"] > 0){
 | 
			
		||||
                            if ($iinfo["grade"] > 0) {
 | 
			
		||||
                                $iinfo["grade"] = format_float($iinfo["grade"], 1). "/".format_float(floatval($iinfo['details']['requirement']));
 | 
			
		||||
                                $iinfo["status"] = $completion->is_complete()?"complete-pass":"complete-fail";
 | 
			
		||||
                                if ($cinfo["status"] == "incomplete"){
 | 
			
		||||
                                if ($cinfo["status"] == "incomplete") {
 | 
			
		||||
                                    $cinfo["status"] = "progress";
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        // finally add the item to the items list
 | 
			
		||||
                        // finally add the item to the items list.
 | 
			
		||||
                        $cinfo["items"][] = $iinfo;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    // Set the count and progress stats based on the Type's aggregation style
 | 
			
		||||
                    if($typeaggregation == COMPLETION_AGGREGATION_ALL){
 | 
			
		||||
                        // Count and Progress amount to the sum of items
 | 
			
		||||
                    // Set the count and progress stats based on the Type's aggregation style.
 | 
			
		||||
                    if ($typeaggregation == COMPLETION_AGGREGATION_ALL) {
 | 
			
		||||
                        // Count and Progress amount to the sum of items.
 | 
			
		||||
                        $cinfo["count"] = count($cinfo["items"]);
 | 
			
		||||
                        $cinfo["progress"] = $progress;
 | 
			
		||||
                    }
 | 
			
		||||
                    else { //$typeaggregation == COMPLETION_AGGREGATION_ANY
 | 
			
		||||
                        // Count and progress are either 1 or 0, since any of the items
 | 
			
		||||
                        // complete's the type
 | 
			
		||||
                    else { //$typeaggregation == COMPLETION_AGGREGATION_ANY.
 | 
			
		||||
                        // Count and progress are either 1 or 0, since any of the items.
 | 
			
		||||
                        // complete's the type.
 | 
			
		||||
                        $cinfo["count"] = (count($cinfo["items"]) > 0)?1:0;
 | 
			
		||||
                        $cinfo["progress"] = ($progress>0)?1:0;
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -426,41 +444,39 @@ class corecompletioninfo {
 | 
			
		|||
     * Get the grade for a certain course module
 | 
			
		||||
     * @return stdClass|null object containing 'grade' and optional 'feedback' attribute
 | 
			
		||||
     */
 | 
			
		||||
    private function get_grade($cm,$userid){
 | 
			
		||||
        // TODO: Display grade in the way described in the course setup (with letters if needed)
 | 
			
		||||
    private function get_grade($cm, $userid) {
 | 
			
		||||
        // TODO: Display grade in the way described in the course setup (with letters if needed).
 | 
			
		||||
 | 
			
		||||
        $gi= grade_item::fetch(['itemtype' => 'mod',
 | 
			
		||||
            'itemmodule' => $cm->modname,
 | 
			
		||||
            'iteminstance' => $cm->instance,
 | 
			
		||||
            'courseid' => $this->course->id]); // Make sure we only get results relevant to this course
 | 
			
		||||
            'courseid' => $this->course->id]); // Make sure we only get results relevant to this course.
 | 
			
		||||
 | 
			
		||||
        if($gi)
 | 
			
		||||
        {
 | 
			
		||||
            // Only the following types of grade yield a result
 | 
			
		||||
            if(($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE))
 | 
			
		||||
            {
 | 
			
		||||
        if ($gi) {
 | 
			
		||||
            // Only the following types of grade yield a result.
 | 
			
		||||
            if (($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE)) {
 | 
			
		||||
                $scale = $gi->load_scale();
 | 
			
		||||
 | 
			
		||||
                $grade = (object)$gi->get_final($userid); // Get the grade for the specified user
 | 
			
		||||
                $grade = (object)$gi->get_final($userid); // Get the grade for the specified user.
 | 
			
		||||
                $result = new \stdClass;
 | 
			
		||||
                // Check if the final grade is available and numeric (safety check)
 | 
			
		||||
                if(!empty($grade) && !empty($grade->finalgrade) && is_numeric($grade->finalgrade)){
 | 
			
		||||
                    // convert scale grades to corresponding scale name
 | 
			
		||||
                    if(isset($scale)){
 | 
			
		||||
                        // get scale value
 | 
			
		||||
                // Check if the final grade is available and numeric (safety check).
 | 
			
		||||
                if (!empty($grade) && !empty($grade->finalgrade) && is_numeric($grade->finalgrade)) {
 | 
			
		||||
                    // convert scale grades to corresponding scale name.
 | 
			
		||||
                    if (isset($scale)) {
 | 
			
		||||
                        // get scale value.
 | 
			
		||||
                        $result->grade = $scale->get_nearest_item($grade->finalgrade);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        // round final grade to 1 decimal point
 | 
			
		||||
                        $result->grade =  round($grade->finalgrade,1);
 | 
			
		||||
                        // round final grade to 1 decimal point.
 | 
			
		||||
                        $result->grade =  round($grade->finalgrade, 1);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    $result->feedback = trim($grade->feedback);
 | 
			
		||||
                    $result->pending = (new gradingscanner($gi))->pending($userid);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    $result->grade = "-"; // Activity is gradable, but user did not receive a grade yet
 | 
			
		||||
                    $result->grade = "-"; // Activity is gradable, but user did not receive a grade yet.
 | 
			
		||||
                    $result->feedback = null;
 | 
			
		||||
                    $result->pending = false;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -468,46 +484,44 @@ class corecompletioninfo {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null; // Activity cannot be graded (Shouldn't be happening, but still....)
 | 
			
		||||
        return null; // Activity cannot be graded (Shouldn't be happening, but still....).
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the grade for a certain course module
 | 
			
		||||
     * @return stdClass|null object containing 'grade' and optional 'feedback' attribute
 | 
			
		||||
     */
 | 
			
		||||
    private function get_course_grade($userid){
 | 
			
		||||
        // TODO: Display grade in the way described in the course setup (with letters if needed)
 | 
			
		||||
    private function get_course_grade($userid) {
 | 
			
		||||
        // TODO: Display grade in the way described in the course setup (with letters if needed).
 | 
			
		||||
        $gi= grade_item::fetch(['itemtype' => 'course',
 | 
			
		||||
            'iteminstance' => $this->course->id,
 | 
			
		||||
            'courseid' => $this->course->id]);
 | 
			
		||||
 | 
			
		||||
        if($gi)
 | 
			
		||||
        {
 | 
			
		||||
            // Only the following types of grade yield a result
 | 
			
		||||
            if(($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE))
 | 
			
		||||
            {
 | 
			
		||||
        if ($gi) {
 | 
			
		||||
            // Only the following types of grade yield a result.
 | 
			
		||||
            if (($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE)) {
 | 
			
		||||
                $scale = $gi->load_scale();
 | 
			
		||||
                $grade = $gi->get_final($userid); // Get the grade for the specified user
 | 
			
		||||
                // Check if the final grade is available and numeric (safety check)
 | 
			
		||||
                if(!empty($grade) && !empty($grade->finalgrade) && is_numeric($grade->finalgrade)){
 | 
			
		||||
                    // convert scale grades to corresponding scale name
 | 
			
		||||
                    if(isset($scale)){
 | 
			
		||||
                        // get scale value
 | 
			
		||||
                $grade = $gi->get_final($userid); // Get the grade for the specified user.
 | 
			
		||||
                // Check if the final grade is available and numeric (safety check).
 | 
			
		||||
                if (!empty($grade) && !empty($grade->finalgrade) && is_numeric($grade->finalgrade)) {
 | 
			
		||||
                    // convert scale grades to corresponding scale name.
 | 
			
		||||
                    if (isset($scale)) {
 | 
			
		||||
                        // get scale value.
 | 
			
		||||
                        return $scale->get_nearest_item($grade->finalgrade);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        // round final grade to 1 decimal point
 | 
			
		||||
                        return round($grade->finalgrade,1);
 | 
			
		||||
                        // round final grade to 1 decimal point.
 | 
			
		||||
                        return round($grade->finalgrade, 1);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    return "-"; // User did not receive a grade yet for this course
 | 
			
		||||
                    return "-"; // User did not receive a grade yet for this course.
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return null; // Course cannot be graded (Shouldn't be happening, but still....)
 | 
			
		||||
        return null; // Course cannot be graded (Shouldn't be happening, but still....).
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -519,7 +533,7 @@ class corecompletioninfo {
 | 
			
		|||
     * @return null|float The percentage, or null if completion is not supported in the course,
 | 
			
		||||
     *         or there are no activities that support completion.
 | 
			
		||||
     */
 | 
			
		||||
    function get_progress_percentage($userid){
 | 
			
		||||
    function get_progress_percentage($userid) {
 | 
			
		||||
 | 
			
		||||
        // First, let's make sure completion is enabled.
 | 
			
		||||
        if (!$this->completion->is_enabled()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -534,18 +548,18 @@ class corecompletioninfo {
 | 
			
		|||
        $count = count($completions);
 | 
			
		||||
        $completed = 0;
 | 
			
		||||
 | 
			
		||||
        // Before we check how many modules have been completed see if the course has completed. 
 | 
			
		||||
        // Before we check how many modules have been completed see if the course has completed. .
 | 
			
		||||
        if ($this->completion->is_course_complete($userid)) {
 | 
			
		||||
            $completed = $count;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // count all completions, but treat 
 | 
			
		||||
            foreach($completions as $completion){
 | 
			
		||||
            // count all completions, but treat .
 | 
			
		||||
            foreach ($completions as $completion) {
 | 
			
		||||
                $crit = $completion->get_criteria();
 | 
			
		||||
                if($crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                    // get the cm data object
 | 
			
		||||
                if ($crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                    // get the cm data object.
 | 
			
		||||
                    $cm = $this->modinfo->get_cm($crit->moduleinstance);
 | 
			
		||||
                    // retrieve data for this object
 | 
			
		||||
                    // retrieve data for this object.
 | 
			
		||||
                    $data = $this->completion->get_data($cm, false, $userid);
 | 
			
		||||
                    // Count complete, but failed as incomplete too...
 | 
			
		||||
                    if (($data->completionstate == COMPLETION_INCOMPLETE) || ($data->completionstate == COMPLETION_COMPLETE_FAIL)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -555,7 +569,7 @@ class corecompletioninfo {
 | 
			
		|||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    if($completion->is_complete()){
 | 
			
		||||
                    if ($completion->is_complete()) {
 | 
			
		||||
                        $completed += 1;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -591,27 +605,27 @@ class corecompletioninfo {
 | 
			
		|||
        $aggregation = $this->completion->get_aggregation_method();
 | 
			
		||||
        $critcount = [];
 | 
			
		||||
 | 
			
		||||
        // Before we check how many modules have been completed see if the course has completed. 
 | 
			
		||||
        // count all completions, but treat 
 | 
			
		||||
        foreach($completions as $completion){
 | 
			
		||||
        // Before we check how many modules have been completed see if the course has completed. .
 | 
			
		||||
        // count all completions, but treat .
 | 
			
		||||
        foreach ($completions as $completion) {
 | 
			
		||||
            $crit = $completion->get_criteria();
 | 
			
		||||
 | 
			
		||||
            // Make a new object for the type if it's not already there
 | 
			
		||||
            // Make a new object for the type if it's not already there.
 | 
			
		||||
            $type = $crit->criteriatype;
 | 
			
		||||
            if(!array_key_exists($type,$critcount)){
 | 
			
		||||
            if (!array_key_exists($type, $critcount)) {
 | 
			
		||||
                $critcount[$type] = new \stdClass;
 | 
			
		||||
                $critcount[$type]->count = 0;
 | 
			
		||||
                $critcount[$type]->completed = 0;
 | 
			
		||||
                $critcount[$type]->aggregation = $this->completion->get_aggregation_method($type);
 | 
			
		||||
            }
 | 
			
		||||
            // Get a reference to the counter object for this type
 | 
			
		||||
            // Get a reference to the counter object for this type.
 | 
			
		||||
            $typecount =& $critcount[$type];
 | 
			
		||||
 | 
			
		||||
            $typecount->count += 1;
 | 
			
		||||
            if($crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                // get the cm data object
 | 
			
		||||
            if ($crit->criteriatype == COMPLETION_CRITERIA_TYPE_ACTIVITY) {
 | 
			
		||||
                // get the cm data object.
 | 
			
		||||
                $cm = $this->modinfo->get_cm($crit->moduleinstance);
 | 
			
		||||
                // retrieve data for this object
 | 
			
		||||
                // retrieve data for this object.
 | 
			
		||||
                $data = $this->completion->get_data($cm, false, $userid);
 | 
			
		||||
                // Count complete, but failed as incomplete too...
 | 
			
		||||
                if (($data->completionstate == COMPLETION_INCOMPLETE) || ($data->completionstate == COMPLETION_COMPLETE_FAIL)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -621,22 +635,22 @@ class corecompletioninfo {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                if($completion->is_complete()){
 | 
			
		||||
                if ($completion->is_complete()) {
 | 
			
		||||
                    $typecount->completed += 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Now that we have all completions sorted by type, we can be smart about how to do the count
 | 
			
		||||
        // Now that we have all completions sorted by type, we can be smart about how to do the count.
 | 
			
		||||
        $count = 0;
 | 
			
		||||
        $completed = 0;
 | 
			
		||||
        $completion_percentage = 0;
 | 
			
		||||
        foreach($critcount as $c){
 | 
			
		||||
            // Take only types that are actually present into account
 | 
			
		||||
            if($c->count > 0){
 | 
			
		||||
                // If the aggregation for the type is ANY, reduce the count to 1 for this type
 | 
			
		||||
                // And adjust the progress accordingly (check if any have been completed or not)
 | 
			
		||||
                if($c->aggregation == COMPLETION_AGGREGATION_ALL){
 | 
			
		||||
        foreach ($critcount as $c) {
 | 
			
		||||
            // Take only types that are actually present into account.
 | 
			
		||||
            if ($c->count > 0) {
 | 
			
		||||
                // If the aggregation for the type is ANY, reduce the count to 1 for this type.
 | 
			
		||||
                // And adjust the progress accordingly (check if any have been completed or not).
 | 
			
		||||
                if ($c->aggregation == COMPLETION_AGGREGATION_ALL) {
 | 
			
		||||
                    $ct = $c->count;
 | 
			
		||||
                    $cmpl = $c->completed;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -644,21 +658,21 @@ class corecompletioninfo {
 | 
			
		|||
                    $ct = 1;
 | 
			
		||||
                    $cmpl = ($c->completed > 0)?1:0;
 | 
			
		||||
                }
 | 
			
		||||
                // if ANY completion for the types, count only the criteria type with the highest completion percentage -
 | 
			
		||||
                // Overwrite data if current type is more complete
 | 
			
		||||
                if($aggregation == COMPLETION_AGGREGATION_ANY) {
 | 
			
		||||
                // if ANY completion for the types, count only the criteria type with the highest completion percentage -.
 | 
			
		||||
                // Overwrite data if current type is more complete.
 | 
			
		||||
                if ($aggregation == COMPLETION_AGGREGATION_ANY) {
 | 
			
		||||
                    $pct = $cmpl/$ct;
 | 
			
		||||
                    if($pct > $completion_percentage){
 | 
			
		||||
                    if ($pct > $completion_percentage) {
 | 
			
		||||
                        $count = $ct;
 | 
			
		||||
                        $completed = $cmpl;
 | 
			
		||||
                        $completion_percentage = $pct;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                // if ALL completion for the types, add the count for this type to that of the others
 | 
			
		||||
                // if ALL completion for the types, add the count for this type to that of the others.
 | 
			
		||||
                else {
 | 
			
		||||
                    $count += $ct;
 | 
			
		||||
                    $completed += $cmpl;
 | 
			
		||||
                    // Don't really care about recalculating completion percentage every round in this case
 | 
			
		||||
                    // Don't really care about recalculating completion percentage every round in this case.
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -20,43 +40,43 @@ class courseinfo {
 | 
			
		|||
    private $studyitem;
 | 
			
		||||
    private static $contentitems = null;
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->course->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function shortname(){
 | 
			
		||||
    public function shortname() {
 | 
			
		||||
        return $this->course->shortname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function course(){
 | 
			
		||||
        return $this->course; // php arrays are assigned by copy
 | 
			
		||||
    public function course() {
 | 
			
		||||
        return $this->course; // php arrays are assigned by copy.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function course_context(){
 | 
			
		||||
    public function course_context() {
 | 
			
		||||
        return $this->coursecontext;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function category_context(){
 | 
			
		||||
    public function category_context() {
 | 
			
		||||
        return $this->context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function get_contentitems() {
 | 
			
		||||
        global $PAGE;
 | 
			
		||||
        if(empty(static::$contentitems)){
 | 
			
		||||
        if (empty(static::$contentitems)) {
 | 
			
		||||
            $PAGE->set_context(\context_system::instance());
 | 
			
		||||
            static::$contentitems = (new content_item_readonly_repository())->find_all();
 | 
			
		||||
        }
 | 
			
		||||
        return static::$contentitems;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function amTeacher(){
 | 
			
		||||
    protected function amTeacher() {
 | 
			
		||||
        global $USER;
 | 
			
		||||
        return is_enrolled($this->coursecontext, $USER, 'mod/assign:grade');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function iCanSelectGradables($userid=-1){
 | 
			
		||||
    protected function iCanSelectGradables($userid=-1) {
 | 
			
		||||
        global $USER, $DB;
 | 
			
		||||
        if($userid <= 0){
 | 
			
		||||
        if ($userid <= 0) {
 | 
			
		||||
            $usr = $USER;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
| 
						 | 
				
			
			@ -68,15 +88,15 @@ class courseinfo {
 | 
			
		|||
 | 
			
		||||
    public static function get_contentitem($name) {
 | 
			
		||||
        $contentitems = static::get_contentitems();
 | 
			
		||||
        for($i = 0; $i < count($contentitems); $i++){
 | 
			
		||||
            if($contentitems[$i]->get_name() == $name){
 | 
			
		||||
        for($i = 0; $i < count($contentitems); $i++) {
 | 
			
		||||
            if ($contentitems[$i]->get_name() == $name) {
 | 
			
		||||
                return $contentitems[$i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct($id,studyitem $studyitem = null){
 | 
			
		||||
    public function __construct($id, studyitem $studyitem = null) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->studyitem = $studyitem;
 | 
			
		||||
        $this->course = \get_course($id);
 | 
			
		||||
| 
						 | 
				
			
			@ -84,23 +104,21 @@ class courseinfo {
 | 
			
		|||
        $this->coursecontext = \context_course::instance($this->course->id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function exists($id){
 | 
			
		||||
    public static function exists($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        return is_numeric($id) && $DB->record_exists(self::TABLE, ['id' => $id]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function id_from_shortname($shortname){
 | 
			
		||||
    public static function id_from_shortname($shortname) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        return $DB->get_field(self::TABLE, "id", ['shortname' => $shortname]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function coursetiming($course){
 | 
			
		||||
    public static function coursetiming($course) {
 | 
			
		||||
        $now = time();
 | 
			
		||||
        if($now > $course->startdate)
 | 
			
		||||
        {
 | 
			
		||||
            if($course->enddate > 0 && $now > $course->enddate)
 | 
			
		||||
            {
 | 
			
		||||
        if ($now > $course->startdate) {
 | 
			
		||||
            if ($course->enddate > 0 && $now > $course->enddate) {
 | 
			
		||||
                return "past";
 | 
			
		||||
            }		
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -112,43 +130,43 @@ class courseinfo {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function timing(){
 | 
			
		||||
    public function timing() {
 | 
			
		||||
        return self::coursetiming($this->course);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function displayname(){
 | 
			
		||||
        $displayfield = get_config("local_treestudyplan","display_field");
 | 
			
		||||
    public function displayname() {
 | 
			
		||||
        $displayfield = get_config("local_treestudyplan", "display_field");
 | 
			
		||||
        if ($displayfield == "idnumber") {
 | 
			
		||||
            $idnumber = trim(preg_replace("/\s+/u", " ",$this->course->idnumber));
 | 
			
		||||
            if(strlen($idnumber) > 0){
 | 
			
		||||
            $idnumber = trim(preg_replace("/\s+/u", " ", $this->course->idnumber));
 | 
			
		||||
            if (strlen($idnumber) > 0) {
 | 
			
		||||
                return $this->course->idnumber;
 | 
			
		||||
            }
 | 
			
		||||
        } else if(strpos( $displayfield ,"customfield_") === 0) {
 | 
			
		||||
            $fieldname = substr($displayfield,strlen("customfield_"));
 | 
			
		||||
        } else if (strpos( $displayfield , "customfield_") === 0) {
 | 
			
		||||
            $fieldname = substr($displayfield, strlen("customfield_"));
 | 
			
		||||
 | 
			
		||||
            $handler = \core_customfield\handler::get_handler('core_course', 'course');
 | 
			
		||||
            $datas = $handler->get_instance_data($this->course->id);
 | 
			
		||||
            foreach($datas as $data){
 | 
			
		||||
                if($data->get_field()->get('shortname') == $fieldname){
 | 
			
		||||
                    $value = trim(preg_replace("/\s+/u", " ",$data->get_value()));
 | 
			
		||||
                    if(strlen($value) > 0){
 | 
			
		||||
            foreach ($datas as $data) {
 | 
			
		||||
                if ($data->get_field()->get('shortname') == $fieldname) {
 | 
			
		||||
                    $value = trim(preg_replace("/\s+/u", " ", $data->get_value()));
 | 
			
		||||
                    if (strlen($value) > 0) {
 | 
			
		||||
                        return $value;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Fallback to shortname when the specified display field fails, since shortname is never empty
 | 
			
		||||
        // Fallback to shortname when the specified display field fails, since shortname is never empty.
 | 
			
		||||
        return $this->course->shortname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function simple_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function simple_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'linked course id'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'linked course name'),
 | 
			
		||||
            "shortname" => new \external_value(PARAM_TEXT, 'linked course shortname'),
 | 
			
		||||
            "displayname" => new \external_value(PARAM_TEXT, 'linked course displayname'),
 | 
			
		||||
            "context" => contextinfo::structure(VALUE_OPTIONAL),
 | 
			
		||||
        ], 'referenced course information',$value);
 | 
			
		||||
        ], 'referenced course information', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function simple_model() {
 | 
			
		||||
| 
						 | 
				
			
			@ -164,7 +182,7 @@ class courseinfo {
 | 
			
		|||
        return $info;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'linked course id'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'linked course name'),
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +190,7 @@ class courseinfo {
 | 
			
		|||
            "displayname" => new \external_value(PARAM_TEXT, 'linked course displayname'),
 | 
			
		||||
            "context" => contextinfo::structure(VALUE_OPTIONAL),
 | 
			
		||||
            "ctxid" => new \external_value(PARAM_INT, 'course context id name'),
 | 
			
		||||
            "grades" => new \external_multiple_structure(gradeinfo::editor_structure(),'grade list (legacy list)',VALUE_OPTIONAL),
 | 
			
		||||
            "grades" => new \external_multiple_structure(gradeinfo::editor_structure(), 'grade list (legacy list)', VALUE_OPTIONAL),
 | 
			
		||||
            "completion" => corecompletioninfo::editor_structure(VALUE_OPTIONAL),
 | 
			
		||||
            "timing" => new \external_value(PARAM_TEXT, '(past|present|future)'),
 | 
			
		||||
            "startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +199,7 @@ class courseinfo {
 | 
			
		|||
            "canupdatecourse" => new \external_value(PARAM_BOOL, "If the current user can update this course"),
 | 
			
		||||
            "canselectgradables" => new \external_value(PARAM_BOOL, 'Requesting user can change selected gradables'),
 | 
			
		||||
            "tag" => new \external_value(PARAM_TEXT, 'Tag'),
 | 
			
		||||
        ], 'referenced course information',$value);
 | 
			
		||||
        ], 'referenced course information', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(studyitem $studyitem=null, $usecorecompletioninfo=false) {
 | 
			
		||||
| 
						 | 
				
			
			@ -198,19 +216,19 @@ class courseinfo {
 | 
			
		|||
            'context' => $contextinfo->model(),
 | 
			
		||||
            'ctxid' => $this->coursecontext->id,
 | 
			
		||||
            'timing' => $timing,
 | 
			
		||||
            'startdate' => date("Y-m-d",$this->course->startdate,), 
 | 
			
		||||
            'enddate' => date("Y-m-d",$this->course->enddate),            
 | 
			
		||||
            'startdate' => date("Y-m-d", $this->course->startdate, ),
 | 
			
		||||
            'enddate' => date("Y-m-d", $this->course->enddate),
 | 
			
		||||
            'amteacher' => $this->amTeacher(),
 | 
			
		||||
            'canupdatecourse' => \has_capability("moodle/course:update",$this->coursecontext),
 | 
			
		||||
            'canupdatecourse' => \has_capability("moodle/course:update", $this->coursecontext),
 | 
			
		||||
            'canselectgradables' =>  $this->iCanSelectGradables(),
 | 
			
		||||
            'tag' => "Editormodel",
 | 
			
		||||
            'grades' => [],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if(!$usecorecompletioninfo){
 | 
			
		||||
            $gradables = gradeinfo::list_course_gradables($this->course,$studyitem);
 | 
			
		||||
        if (!$usecorecompletioninfo) {
 | 
			
		||||
            $gradables = gradeinfo::list_course_gradables($this->course, $studyitem);
 | 
			
		||||
 | 
			
		||||
            foreach($gradables as $gradable) {
 | 
			
		||||
            foreach ($gradables as $gradable) {
 | 
			
		||||
                $info['grades'][] = $gradable->editor_model($studyitem);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -222,7 +240,7 @@ class courseinfo {
 | 
			
		|||
        return $info;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'linked course id'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'linked course name'),
 | 
			
		||||
| 
						 | 
				
			
			@ -230,15 +248,15 @@ class courseinfo {
 | 
			
		|||
            "displayname" => new \external_value(PARAM_TEXT, 'linked course displayname'),
 | 
			
		||||
            "context" => contextinfo::structure(VALUE_OPTIONAL),
 | 
			
		||||
            "ctxid" => new \external_value(PARAM_INT, 'course context id name'),
 | 
			
		||||
            "grades" => new \external_multiple_structure(gradeinfo::user_structure(),'grade list (legacy list)',VALUE_OPTIONAL),
 | 
			
		||||
            "grades" => new \external_multiple_structure(gradeinfo::user_structure(), 'grade list (legacy list)', VALUE_OPTIONAL),
 | 
			
		||||
            "completion" => corecompletioninfo::user_structure(VALUE_OPTIONAL),
 | 
			
		||||
            "timing" => new \external_value(PARAM_TEXT, '(past|present|future)'),
 | 
			
		||||
            "startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
 | 
			
		||||
            "enddate" => new \external_value(PARAM_TEXT, 'Course end date'),
 | 
			
		||||
        ], 'course information',$value);
 | 
			
		||||
        ], 'course information', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid,$usecorecompletioninfo=false) {
 | 
			
		||||
    public function user_model($userid, $usecorecompletioninfo=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $contextinfo = new contextinfo($this->context);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -252,14 +270,14 @@ class courseinfo {
 | 
			
		|||
            'context' => $contextinfo->model(),
 | 
			
		||||
            'ctxid' => $this->coursecontext->id,
 | 
			
		||||
            'timing' => $timing,
 | 
			
		||||
            'startdate' => date("Y-m-d",$this->course->startdate), 
 | 
			
		||||
            'enddate' => date("Y-m-d",$this->course->enddate),
 | 
			
		||||
            'startdate' => date("Y-m-d", $this->course->startdate),
 | 
			
		||||
            'enddate' => date("Y-m-d", $this->course->enddate),
 | 
			
		||||
            'grades' => [],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if(!$usecorecompletioninfo){
 | 
			
		||||
        if (!$usecorecompletioninfo) {
 | 
			
		||||
            $gradables = gradeinfo::list_studyitem_gradables($this->studyitem);
 | 
			
		||||
            foreach($gradables as $gi) {
 | 
			
		||||
            foreach ($gradables as $gi) {
 | 
			
		||||
                $info['grades'][] = $gi->user_model($userid);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -15,22 +35,22 @@ class coursemoduleinfo {
 | 
			
		|||
    private $cm_info;
 | 
			
		||||
    private $db_record;
 | 
			
		||||
 | 
			
		||||
    public function __construct($id){
 | 
			
		||||
    public function __construct($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        // Determine the icon for the associated activity
 | 
			
		||||
        // Determine the icon for the associated activity.
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->cm = $DB->get_record("course_modules",["id" => $id]);
 | 
			
		||||
        $this->cm = $DB->get_record("course_modules", ["id" => $id]);
 | 
			
		||||
        $this->cm_info = \cm_info::create($this->cm);
 | 
			
		||||
        // $this->db_record = $DB->get_record($this->cm_info->modname,["id" => $this->cm_info->instance]);
 | 
			
		||||
        // $this->db_record = $DB->get_record($this->cm_info->modname, ["id" => $this->cm_info->instance]);.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getTitle(){
 | 
			
		||||
    public function getTitle() {
 | 
			
		||||
        return $this->cm_info->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTitle($value){
 | 
			
		||||
    public function setTitle($value) {
 | 
			
		||||
        $this->cm_info->set_name($value);
 | 
			
		||||
        // TODO: Actually save this after setting the cminfo
 | 
			
		||||
        // TODO: Actually save this after setting the cminfo.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,48 +52,47 @@ class courseservice extends \external_api
 | 
			
		|||
        return new \external_multiple_structure(static::map_category_structure(false));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected static function map_category_structure($lazy=false,$value=VALUE_REQUIRED)
 | 
			
		||||
    {
 | 
			
		||||
    protected static function map_category_structure($lazy=false, $value=VALUE_REQUIRED) {
 | 
			
		||||
        $s = [
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'course category id'),
 | 
			
		||||
            "context_id" => new \external_value(PARAM_INT, 'course category context id'),
 | 
			
		||||
            "category" =>  contextinfo::structure(VALUE_OPTIONAL),
 | 
			
		||||
            "haschildren" => new \external_value(PARAM_BOOL, 'True if the category has child categories'),
 | 
			
		||||
            "hascourses" => new \external_value(PARAM_BOOL, 'True if the category contains courses'),
 | 
			
		||||
            "studyplancount" =>  new \external_value(PARAM_INT, 'number of linked studyplans',VALUE_OPTIONAL),
 | 
			
		||||
            "studyplancount" =>  new \external_value(PARAM_INT, 'number of linked studyplans', VALUE_OPTIONAL),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if(!$lazy > 0) {
 | 
			
		||||
        if (!$lazy > 0) {
 | 
			
		||||
            $s["courses"] = new \external_multiple_structure( courseinfo::editor_structure() );
 | 
			
		||||
            $s["children"] = new \external_multiple_structure( static::map_category_structure(true));
 | 
			
		||||
        }
 | 
			
		||||
        return  new \external_single_structure($s,"CourseCat info",$value);
 | 
			
		||||
        return  new \external_single_structure($s, "CourseCat info", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function map_categories($root_id = 0){
 | 
			
		||||
    public static function map_categories($root_id = 0) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $root = \core_course_category::get($root_id);
 | 
			
		||||
        $context = $root->get_context();
 | 
			
		||||
        // Make sure the user has access to the context for editing purposes
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT,$context);
 | 
			
		||||
        // Make sure the user has access to the context for editing purposes.
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_EDIT, $context);
 | 
			
		||||
 | 
			
		||||
        // Determine top categories from provided context
 | 
			
		||||
        // Determine top categories from provided context.
 | 
			
		||||
 | 
			
		||||
        if($root->id == 0){
 | 
			
		||||
            // on the system level, determine the user's topmost allowed catecories
 | 
			
		||||
        if ($root->id == 0) {
 | 
			
		||||
            // on the system level, determine the user's topmost allowed catecories.
 | 
			
		||||
            $user_top = \core_course_category::user_top();
 | 
			
		||||
            if($user_top->id == 0){ // top category..
 | 
			
		||||
                $children = $root->get_children(); // returns a list of çore_course_category, let it overwrite $children
 | 
			
		||||
            if ($user_top->id == 0) { // top category..
 | 
			
		||||
                $children = $root->get_children(); // returns a list of çore_course_category, let it overwrite $children.
 | 
			
		||||
            } else {
 | 
			
		||||
                $children = [$user_top];
 | 
			
		||||
            }
 | 
			
		||||
        } else if ($root->is_uservisible()){
 | 
			
		||||
        } else if ($root->is_uservisible()) {
 | 
			
		||||
            $children = [$root];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach($children as $cat){
 | 
			
		||||
            $list[] = static::map_category($cat,false);
 | 
			
		||||
        foreach ($children as $cat) {
 | 
			
		||||
            $list[] = static::map_category($cat, false);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -89,16 +109,16 @@ class courseservice extends \external_api
 | 
			
		|||
        return static::map_category_structure(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function get_category($id){
 | 
			
		||||
    public static function get_category($id) {
 | 
			
		||||
        $cat = \core_course_category::get($id);
 | 
			
		||||
        return static::map_category($cat);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected static function map_category(\core_course_category $cat,$lazy=false){
 | 
			
		||||
    protected static function map_category(\core_course_category $cat, $lazy=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $catcontext = $cat->get_context();
 | 
			
		||||
        $ctx_info = new contextinfo($catcontext);
 | 
			
		||||
        $children = $cat->get_children(); // only shows children visible to the current user
 | 
			
		||||
        $children = $cat->get_children(); // only shows children visible to the current user.
 | 
			
		||||
        $courses = $cat->get_courses();
 | 
			
		||||
        $model = [
 | 
			
		||||
            "id" => $cat->id,
 | 
			
		||||
| 
						 | 
				
			
			@ -108,17 +128,16 @@ class courseservice extends \external_api
 | 
			
		|||
            "hascourses" => !empty($courses),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        if(!$lazy)
 | 
			
		||||
        {
 | 
			
		||||
        if (!$lazy) {
 | 
			
		||||
            $model["courses"] = [];
 | 
			
		||||
            foreach($courses as $course){
 | 
			
		||||
            foreach ($courses as $course) {
 | 
			
		||||
                $courseinfo = new courseinfo($course->id);
 | 
			
		||||
                $model["courses"][] = $courseinfo->editor_model();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $model["children"] = [];
 | 
			
		||||
            foreach($children as $child){
 | 
			
		||||
                $model["children"][] = static::map_category($child,true);
 | 
			
		||||
            foreach ($children as $child) {
 | 
			
		||||
                $model["children"][] = static::map_category($child, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +147,7 @@ class courseservice extends \external_api
 | 
			
		|||
    public static function list_accessible_categories_parameters()
 | 
			
		||||
	{
 | 
			
		||||
        return new \external_function_parameters( [
 | 
			
		||||
            "operation" => new \external_value(PARAM_TEXT, 'type of operation ["view"|"edit"]',VALUE_DEFAULT),] 
 | 
			
		||||
            "operation" => new \external_value(PARAM_TEXT, 'type of operation ["view"|"edit"]', VALUE_DEFAULT), ]
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -139,9 +158,9 @@ class courseservice extends \external_api
 | 
			
		|||
 | 
			
		||||
    public static function list_accessible_categories($operation="edit")
 | 
			
		||||
	{
 | 
			
		||||
        if($operation == "edit"){
 | 
			
		||||
        if ($operation == "edit") {
 | 
			
		||||
            $capability = self::CAP_EDIT;
 | 
			
		||||
        } else { // $operation == "view" || default
 | 
			
		||||
        } else { // $operation == "view" || default.
 | 
			
		||||
            $capability = self::CAP_VIEW;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -149,35 +168,35 @@ class courseservice extends \external_api
 | 
			
		|||
 | 
			
		||||
        $list = [];
 | 
			
		||||
        /* @var $cat \core_course_category */
 | 
			
		||||
        foreach($cats as $cat){
 | 
			
		||||
            $list[] = static::map_category($cat,true);
 | 
			
		||||
        foreach ($cats as $cat) {
 | 
			
		||||
            $list[] = static::map_category($cat, true);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function categories_by_capability($capability,\core_course_category $parent=null){
 | 
			
		||||
        // List the categories in which the user has a specific capability
 | 
			
		||||
    public static function categories_by_capability($capability, \core_course_category $parent=null) {
 | 
			
		||||
        // List the categories in which the user has a specific capability.
 | 
			
		||||
        $list = [];
 | 
			
		||||
        // initialize parent if needed
 | 
			
		||||
        if($parent == null){
 | 
			
		||||
        // initialize parent if needed.
 | 
			
		||||
        if ($parent == null) {
 | 
			
		||||
            $parent = \core_course_category::user_top();
 | 
			
		||||
            if(has_capability($capability,$parent->get_context())){
 | 
			
		||||
            if (has_capability($capability, $parent->get_context())) {
 | 
			
		||||
                $list[] = $parent;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $children = $parent->get_children();
 | 
			
		||||
        foreach($children as $child){
 | 
			
		||||
            // Check if we should add this category
 | 
			
		||||
            if(has_capability($capability,$child->get_context())){
 | 
			
		||||
        foreach ($children as $child) {
 | 
			
		||||
            // Check if we should add this category.
 | 
			
		||||
            if (has_capability($capability, $child->get_context())) {
 | 
			
		||||
                $list[] = $child;
 | 
			
		||||
                // For optimization purposes, we include all its children now, since they will have inherited the permission
 | 
			
		||||
                // #PREMATURE_OPTIMIZATION  ???
 | 
			
		||||
                $list = array_merge($list,self::recursive_child_categories($child));
 | 
			
		||||
                // For optimization purposes, we include all its children now, since they will have inherited the permission.
 | 
			
		||||
                // #PREMATURE_OPTIMIZATION  ???.
 | 
			
		||||
                $list = array_merge($list, self::recursive_child_categories($child));
 | 
			
		||||
            } else {
 | 
			
		||||
                if($child->get_children_count() > 0){
 | 
			
		||||
                    $list = array_merge($list,self::categories_by_capability($capability,$child));
 | 
			
		||||
                if ($child->get_children_count() > 0) {
 | 
			
		||||
                    $list = array_merge($list, self::categories_by_capability($capability, $child));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -185,13 +204,13 @@ class courseservice extends \external_api
 | 
			
		|||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected static function recursive_child_categories(\core_course_category $parent){
 | 
			
		||||
    protected static function recursive_child_categories(\core_course_category $parent) {
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $children = $parent->get_children();
 | 
			
		||||
        foreach($children as $child){
 | 
			
		||||
        foreach ($children as $child) {
 | 
			
		||||
            $list[] = $child;
 | 
			
		||||
            if($child->get_children_count() > 0){
 | 
			
		||||
                $list = array_merge($list,self::recursive_child_categories($child));
 | 
			
		||||
            if ($child->get_children_count() > 0) {
 | 
			
		||||
                $list = array_merge($list, self::recursive_child_categories($child));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +219,7 @@ class courseservice extends \external_api
 | 
			
		|||
    public static function list_used_categories_parameters()
 | 
			
		||||
	{
 | 
			
		||||
        return new \external_function_parameters( [
 | 
			
		||||
            "operation" => new \external_value(PARAM_TEXT, 'type of operation ["view"|"edit"]',VALUE_DEFAULT),
 | 
			
		||||
            "operation" => new \external_value(PARAM_TEXT, 'type of operation ["view"|"edit"]', VALUE_DEFAULT),
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -212,64 +231,64 @@ class courseservice extends \external_api
 | 
			
		|||
    public static function list_used_categories($operation='edit')
 | 
			
		||||
	{
 | 
			
		||||
        global $DB;
 | 
			
		||||
        if($operation == "edit"){
 | 
			
		||||
        if ($operation == "edit") {
 | 
			
		||||
            $capability = self::CAP_EDIT;
 | 
			
		||||
        } else { // $operation == "view" || default
 | 
			
		||||
        } else { // $operation == "view" || default.
 | 
			
		||||
            $capability = self::CAP_VIEW;
 | 
			
		||||
        }
 | 
			
		||||
        $context_ids = [];
 | 
			
		||||
        $rs = $DB->get_recordset_sql("SELECT DISTINCT context_id, COUNT(*) as num FROM {local_treestudyplan}
 | 
			
		||||
                                         GROUP BY context_id");
 | 
			
		||||
        foreach($rs as $r){
 | 
			
		||||
        foreach ($rs as $r) {
 | 
			
		||||
            $context_ids[$r->context_id] = $r->num;
 | 
			
		||||
        }
 | 
			
		||||
        $rs->close();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Now filter the categories that the user has acces to by the used context id's
 | 
			
		||||
        // (That should filter out irrelevant stuff)
 | 
			
		||||
        // Now filter the categories that the user has acces to by the used context id's.
 | 
			
		||||
        // (That should filter out irrelevant stuff).
 | 
			
		||||
        $cats = static::categories_by_capability($capability);
 | 
			
		||||
 | 
			
		||||
        $list = [];
 | 
			
		||||
        foreach($cats as $cat){
 | 
			
		||||
        foreach ($cats as $cat) {
 | 
			
		||||
            $count = 0;
 | 
			
		||||
            $ctxid = $cat->get_context()->id;
 | 
			
		||||
            if(array_key_exists($ctxid,$context_ids)){
 | 
			
		||||
            if (array_key_exists($ctxid, $context_ids)) {
 | 
			
		||||
                $count = $context_ids[$ctxid];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            $o = static::map_category($cat,true);
 | 
			
		||||
            $o = static::map_category($cat, true);
 | 
			
		||||
            $o["studyplancount"] = $count;
 | 
			
		||||
            $list[] = $o;
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list_accessible_categories_with_usage($operation='edit'){
 | 
			
		||||
    public static function list_accessible_categories_with_usage($operation='edit') {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        if($operation == "edit"){
 | 
			
		||||
        if ($operation == "edit") {
 | 
			
		||||
            $capability = self::CAP_EDIT;
 | 
			
		||||
        } else { // $operation == "view" || default
 | 
			
		||||
        } else { // $operation == "view" || default.
 | 
			
		||||
            $capability = self::CAP_VIEW;
 | 
			
		||||
        }
 | 
			
		||||
        // retrieve context ids used
 | 
			
		||||
        // retrieve context ids used.
 | 
			
		||||
        $context_ids = [];
 | 
			
		||||
        $rs = $DB->get_recordset_sql("SELECT DISTINCT context_id, COUNT(*) as num FROM {local_treestudyplan}
 | 
			
		||||
                                         GROUP BY context_id");
 | 
			
		||||
        foreach($rs as $r){
 | 
			
		||||
        foreach ($rs as $r) {
 | 
			
		||||
            $context_ids[$r->context_id] = $r->num;
 | 
			
		||||
        }
 | 
			
		||||
        $rs->close();
 | 
			
		||||
 | 
			
		||||
        // Now filter the categories that the user has acces to by the used context id's
 | 
			
		||||
        // (That should filter out irrelevant stuff)
 | 
			
		||||
        // Now filter the categories that the user has acces to by the used context id's.
 | 
			
		||||
        // (That should filter out irrelevant stuff).
 | 
			
		||||
        $cats = static::categories_by_capability($capability);
 | 
			
		||||
 | 
			
		||||
        $list = [];
 | 
			
		||||
        foreach($cats as $cat){
 | 
			
		||||
        foreach ($cats as $cat) {
 | 
			
		||||
            $count = 0;
 | 
			
		||||
            $ctxid = $cat->get_context()->id;
 | 
			
		||||
            if(array_key_exists($ctxid,$context_ids)){
 | 
			
		||||
            if (array_key_exists($ctxid, $context_ids)) {
 | 
			
		||||
                $count = $context_ids[$ctxid];
 | 
			
		||||
            }
 | 
			
		||||
            $o = new \stdClass();
 | 
			
		||||
| 
						 | 
				
			
			@ -288,33 +307,30 @@ class courseservice extends \external_api
 | 
			
		|||
     *
 | 
			
		||||
     **************************************/
 | 
			
		||||
 | 
			
		||||
    public static function scan_grade_progress_parameters() 
 | 
			
		||||
    {
 | 
			
		||||
    public static function scan_grade_progress_parameters() {
 | 
			
		||||
        return new \external_function_parameters( [
 | 
			
		||||
            "gradeitemid" => new \external_value(PARAM_INT, 'Grade item ID to scan progress for',VALUE_DEFAULT),
 | 
			
		||||
            "studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in',VALUE_DEFAULT),
 | 
			
		||||
            "gradeitemid" => new \external_value(PARAM_INT, 'Grade item ID to scan progress for', VALUE_DEFAULT),
 | 
			
		||||
            "studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in', VALUE_DEFAULT),
 | 
			
		||||
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function scan_grade_progress_returns() 
 | 
			
		||||
    {
 | 
			
		||||
    public static function scan_grade_progress_returns() {
 | 
			
		||||
    return gradingscanner::structure(VALUE_REQUIRED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function scan_grade_progress($gradeitemid, $studyplanid) 
 | 
			
		||||
    {
 | 
			
		||||
    public static function scan_grade_progress($gradeitemid, $studyplanid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        // Verify access to the study plan
 | 
			
		||||
        // Verify access to the study plan.
 | 
			
		||||
        $o = studyplan::findById($studyplanid);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW,$o->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW, $o->context());
 | 
			
		||||
 | 
			
		||||
        // Retrieve grade item
 | 
			
		||||
        // Retrieve grade item.
 | 
			
		||||
        $gi = \grade_item::fetch(["id" => $gradeitemid]);
 | 
			
		||||
 | 
			
		||||
        // Validate course is linked to studyplan
 | 
			
		||||
        // Validate course is linked to studyplan.
 | 
			
		||||
        $courseid = $gi->courseid;
 | 
			
		||||
        if(!$o->course_linked($courseid)){
 | 
			
		||||
        if (!$o->course_linked($courseid)) {
 | 
			
		||||
            throw new \webservice_access_exception("Course {$courseid} linked to grade item {$gradeitemid} is not linked to studyplan {$o->id()}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -326,9 +342,9 @@ class courseservice extends \external_api
 | 
			
		|||
    public static function scan_completion_progress_parameters()
 | 
			
		||||
	{
 | 
			
		||||
        return new \external_function_parameters( [
 | 
			
		||||
            "criteriaid" => new \external_value(PARAM_INT, 'CriteriaID to scan progress for',VALUE_DEFAULT),
 | 
			
		||||
            "studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in',VALUE_DEFAULT),
 | 
			
		||||
            "courseid" => new \external_value(PARAM_INT, 'Course id of criteria',VALUE_DEFAULT),
 | 
			
		||||
            "criteriaid" => new \external_value(PARAM_INT, 'CriteriaID to scan progress for', VALUE_DEFAULT),
 | 
			
		||||
            "studyplanid" => new \external_value(PARAM_INT, 'Study plan id to check progress in', VALUE_DEFAULT),
 | 
			
		||||
            "courseid" => new \external_value(PARAM_INT, 'Course id of criteria', VALUE_DEFAULT),
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -337,26 +353,26 @@ class courseservice extends \external_api
 | 
			
		|||
        return completionscanner::structure(VALUE_REQUIRED);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function scan_completion_progress($criteriaid, $studyplanid,$courseid) 
 | 
			
		||||
    public static function scan_completion_progress($criteriaid, $studyplanid, $courseid)
 | 
			
		||||
	{
 | 
			
		||||
        global $DB;
 | 
			
		||||
        // Verify access to the study plan
 | 
			
		||||
        // Verify access to the study plan.
 | 
			
		||||
        $o = studyplan::findById($studyplanid);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW,$o->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW, $o->context());
 | 
			
		||||
 | 
			
		||||
        $crit = \completion_criteria::fetch(["id" => $criteriaid]);
 | 
			
		||||
        if(!$o->course_linked($courseid)){
 | 
			
		||||
        if (!$o->course_linked($courseid)) {
 | 
			
		||||
            throw new \webservice_access_exception("Course {$courseid} linked to criteria {$criteriaid} is not linked to studyplan {$o->id()}");
 | 
			
		||||
        }
 | 
			
		||||
        $scanner = new completionscanner($crit,\get_course($courseid));
 | 
			
		||||
        $scanner = new completionscanner($crit, \get_course($courseid));
 | 
			
		||||
        return $scanner->model();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function scan_badge_progress_parameters()
 | 
			
		||||
	{
 | 
			
		||||
        return new \external_function_parameters( [
 | 
			
		||||
            "badgeid" => new \external_value(PARAM_INT, 'Badge to scan progress for',VALUE_DEFAULT),
 | 
			
		||||
            "studyplanid" => new \external_value(PARAM_INT, 'Study plan id to limit progress search to (to determine which students to scan)',VALUE_DEFAULT),
 | 
			
		||||
            "badgeid" => new \external_value(PARAM_INT, 'Badge to scan progress for', VALUE_DEFAULT),
 | 
			
		||||
            "studyplanid" => new \external_value(PARAM_INT, 'Study plan id to limit progress search to (to determine which students to scan)', VALUE_DEFAULT),
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -368,26 +384,26 @@ class courseservice extends \external_api
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function scan_badge_progress($badgeid,$studyplanid) 
 | 
			
		||||
    public static function scan_badge_progress($badgeid, $studyplanid)
 | 
			
		||||
	{
 | 
			
		||||
        global $DB;
 | 
			
		||||
        // Check access to the study plan
 | 
			
		||||
        // Check access to the study plan.
 | 
			
		||||
        $o = studyplan::findById($studyplanid);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW,$o->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEW, $o->context());
 | 
			
		||||
 | 
			
		||||
        // Validate that badge is linked to studyplan
 | 
			
		||||
        if(!$o->badge_linked($badgeid)){
 | 
			
		||||
        // Validate that badge is linked to studyplan.
 | 
			
		||||
        if (!$o->badge_linked($badgeid)) {
 | 
			
		||||
            throw new \webservice_access_exception("Badge {$badgeid} is not linked to studyplan {$o->id()}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get badge info
 | 
			
		||||
        // Get badge info.
 | 
			
		||||
        $badge = new \core_badges\badge($badgeid);
 | 
			
		||||
        $badgeinfo = new badgeinfo($badge);
 | 
			
		||||
 | 
			
		||||
        // get the connected users
 | 
			
		||||
        // get the connected users.
 | 
			
		||||
        $students = associationservice::all_associated($studyplanid);
 | 
			
		||||
        // Just get the user ids
 | 
			
		||||
        $studentids = array_map(function ($a){ return $a["id"];},$students);
 | 
			
		||||
        // Just get the user ids.
 | 
			
		||||
        $studentids = array_map(function ($a) { return $a["id"];}, $students);
 | 
			
		||||
 | 
			
		||||
        return [
 | 
			
		||||
            "total" => count($studentids),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -36,29 +56,29 @@ class gradeinfo {
 | 
			
		|||
 | 
			
		||||
    private static $sections = [];
 | 
			
		||||
 | 
			
		||||
    protected static function getSectionSequence($sectionid){
 | 
			
		||||
    protected static function getSectionSequence($sectionid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        if(!array_key_exists($sectionid,self::$sections)){
 | 
			
		||||
            self::$sections[$sectionid] = explode(",",$DB->get_field("course_sections","sequence",["id"=>$sectionid]));
 | 
			
		||||
        if (!array_key_exists($sectionid, self::$sections)) {
 | 
			
		||||
            self::$sections[$sectionid] = explode(", ", $DB->get_field("course_sections", "sequence", ["id"=>$sectionid]));
 | 
			
		||||
        }
 | 
			
		||||
        return self::$sections[$sectionid];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getGradeitem(){
 | 
			
		||||
    public function getGradeitem() {
 | 
			
		||||
        return $this->gradeitem;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getGradingscanner(){
 | 
			
		||||
    public function getGradingscanner() {
 | 
			
		||||
        return $this->gradingscanner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getScale(){
 | 
			
		||||
    public function getScale() {
 | 
			
		||||
        return $this->scale;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected static function get_contentitems() {
 | 
			
		||||
        global $PAGE;
 | 
			
		||||
        if(empty(static::$contentitems)){
 | 
			
		||||
        if (empty(static::$contentitems)) {
 | 
			
		||||
            $PAGE->set_context(\context_system::instance());
 | 
			
		||||
            static::$contentitems = (new content_item_readonly_repository())->find_all();
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -67,60 +87,58 @@ class gradeinfo {
 | 
			
		|||
 | 
			
		||||
    public static function get_contentitem($name) {
 | 
			
		||||
        $contentitems = static::get_contentitems();
 | 
			
		||||
        for($i = 0; $i < count($contentitems); $i++){
 | 
			
		||||
            if($contentitems[$i]->get_name() == $name){
 | 
			
		||||
        for($i = 0; $i < count($contentitems); $i++) {
 | 
			
		||||
            if ($contentitems[$i]->get_name() == $name) {
 | 
			
		||||
                return $contentitems[$i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function getCourseContextById($id){
 | 
			
		||||
    public static function getCourseContextById($id) {
 | 
			
		||||
        $gi = grade_item::fetch(["id" => $id]);
 | 
			
		||||
        if(!$gi || course_module_instance_pending_deletion($gi->courseid, $gi->itemmodule, $gi->iteminstance))
 | 
			
		||||
        {
 | 
			
		||||
            throw new \InvalidArgumentException ("Grade {$id} not found in database". print_r($gi,true));
 | 
			
		||||
        if (!$gi || course_module_instance_pending_deletion($gi->courseid, $gi->itemmodule, $gi->iteminstance)) {
 | 
			
		||||
            throw new \InvalidArgumentException ("Grade {$id} not found in database". print_r($gi, true));
 | 
			
		||||
        }
 | 
			
		||||
        return \context_course::instance($gi->courseid);;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct($id,studyitem $studyitem = null){
 | 
			
		||||
    public function __construct($id, studyitem $studyitem = null) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->studyitem = $studyitem;
 | 
			
		||||
 | 
			
		||||
        $gi = grade_item::fetch(["id" => $id]);
 | 
			
		||||
        if(!$gi || course_module_instance_pending_deletion($gi->courseid, $gi->itemmodule, $gi->iteminstance))
 | 
			
		||||
        {
 | 
			
		||||
            throw new \InvalidArgumentException ("Grade {$id} not found in database". print_r($gi,true));
 | 
			
		||||
        if (!$gi || course_module_instance_pending_deletion($gi->courseid, $gi->itemmodule, $gi->iteminstance)) {
 | 
			
		||||
            throw new \InvalidArgumentException ("Grade {$id} not found in database". print_r($gi, true));
 | 
			
		||||
        }
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->gradeitem = $gi;
 | 
			
		||||
 | 
			
		||||
        // Determine the icon for the associated activity
 | 
			
		||||
        // Determine the icon for the associated activity.
 | 
			
		||||
        $contentitem = static::get_contentitem($gi->itemmodule);
 | 
			
		||||
        $this->icon = empty($contentitem)?"":$contentitem->get_icon();
 | 
			
		||||
 | 
			
		||||
        // Determine a link to the associated activity
 | 
			
		||||
        if($gi->itemtype != "mod" || empty($gi->itemmodule) || empty($gi->iteminstance)){
 | 
			
		||||
        // Determine a link to the associated activity.
 | 
			
		||||
        if ($gi->itemtype != "mod" || empty($gi->itemmodule) || empty($gi->iteminstance)) {
 | 
			
		||||
            $this->link = "";
 | 
			
		||||
            $this->cmid = 0;
 | 
			
		||||
            $this->section = 0;
 | 
			
		||||
            $this->sectionorder = 0;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
 | 
			
		||||
            list($c, $cminfo) = get_course_and_cm_from_instance($gi->iteminstance, $gi->itemmodule);
 | 
			
		||||
            $this->cmid = $cminfo->id;
 | 
			
		||||
            // sort by position in course
 | 
			
		||||
            // 
 | 
			
		||||
            // sort by position in course.
 | 
			
		||||
            // .
 | 
			
		||||
            $this->section = $cminfo->sectionnum;
 | 
			
		||||
            $ssequence = self::getSectionSequence($cminfo->section);
 | 
			
		||||
            $this->sectionorder = array_search($cminfo->id,$ssequence);
 | 
			
		||||
            $this->sectionorder = array_search($cminfo->id, $ssequence);
 | 
			
		||||
 | 
			
		||||
            $this->link = "/mod/{$gi->itemmodule}/view.php?id={$cminfo->id}";
 | 
			
		||||
            if($gi->itemmodule == 'quiz'){
 | 
			
		||||
            if ($gi->itemmodule == 'quiz') {
 | 
			
		||||
                $this->gradinglink = "/mod/{$gi->itemmodule}/report.php?id={$cminfo->id}&mode=grading";
 | 
			
		||||
            }
 | 
			
		||||
            else if($gi->itemmodule == "assign") { 
 | 
			
		||||
            else if ($gi->itemmodule == "assign") {
 | 
			
		||||
                $this->gradinglink = $this->link ."&action=grading";
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -141,31 +159,31 @@ class gradeinfo {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function is_selected(){
 | 
			
		||||
    public function is_selected() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        if($this->studyitem){
 | 
			
		||||
            // Check if selected for this studyitem
 | 
			
		||||
            $r = $DB->get_record('local_treestudyplan_gradeinc',['studyitem_id' => $this->studyitem->id(), 'grade_item_id'=> $this->gradeitem->id]);
 | 
			
		||||
            if($r && $r->include) {
 | 
			
		||||
        if ($this->studyitem) {
 | 
			
		||||
            // Check if selected for this studyitem.
 | 
			
		||||
            $r = $DB->get_record('local_treestudyplan_gradeinc', ['studyitem_id' => $this->studyitem->id(), 'grade_item_id'=> $this->gradeitem->id]);
 | 
			
		||||
            if ($r && $r->include) {
 | 
			
		||||
                return(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function is_required(){
 | 
			
		||||
    public function is_required() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        if($this->studyitem){
 | 
			
		||||
            // Check if selected for this studyitem
 | 
			
		||||
            $r = $DB->get_record('local_treestudyplan_gradeinc',['studyitem_id' => $this->studyitem->id(), 'grade_item_id'=> $this->gradeitem->id]);
 | 
			
		||||
            if($r && $r->include && $r->required) {
 | 
			
		||||
        if ($this->studyitem) {
 | 
			
		||||
            // Check if selected for this studyitem.
 | 
			
		||||
            $r = $DB->get_record('local_treestudyplan_gradeinc', ['studyitem_id' => $this->studyitem->id(), 'grade_item_id'=> $this->gradeitem->id]);
 | 
			
		||||
            if ($r && $r->include && $r->required) {
 | 
			
		||||
                return(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'grade_item id'),
 | 
			
		||||
            "cmid" => new \external_value(PARAM_INT, 'course module id'),
 | 
			
		||||
| 
						 | 
				
			
			@ -178,7 +196,7 @@ class gradeinfo {
 | 
			
		|||
            "gradinglink" => new \external_value(PARAM_TEXT, 'link to related activity'),
 | 
			
		||||
            "grading" => gradingscanner::structure(),
 | 
			
		||||
            "required" =>  new \external_value(PARAM_BOOL, 'is required for current studyitem'),
 | 
			
		||||
        ], 'referenced course information',$value);
 | 
			
		||||
        ], 'referenced course information', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(studyitem $studyitem = null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -195,15 +213,15 @@ class gradeinfo {
 | 
			
		|||
            "required" =>  $this->is_required(),
 | 
			
		||||
        ];
 | 
			
		||||
        // Unfortunately, lazy loading of the completion data is off, since we need the data to show study item completion...
 | 
			
		||||
        if($studyitem !== null && $this->is_selected() && has_capability('local/treestudyplan:viewuserreports',$studyitem->studyline()->studyplan()->context()) 
 | 
			
		||||
            && $this->gradingscanner->is_available()){
 | 
			
		||||
        if ($studyitem !== null && $this->is_selected() && has_capability('local/treestudyplan:viewuserreports', $studyitem->studyline()->studyplan()->context())
 | 
			
		||||
            && $this->gradingscanner->is_available()) {
 | 
			
		||||
            $model['grading'] = $this->gradingscanner->model();
 | 
			
		||||
        }
 | 
			
		||||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'grade_item id'),
 | 
			
		||||
            "cmid" => new \external_value(PARAM_INT, 'course module id'),
 | 
			
		||||
| 
						 | 
				
			
			@ -215,26 +233,26 @@ class gradeinfo {
 | 
			
		|||
            "completion" =>  new \external_value(PARAM_TEXT, 'completion state (incomplete|progress|completed|excellent)'),
 | 
			
		||||
            "icon" => new \external_value(PARAM_RAW, 'html for icon of related activity'),
 | 
			
		||||
            "link" => new \external_value(PARAM_TEXT, 'link to related activity'),
 | 
			
		||||
            "pendingsubmission" =>  new \external_value(PARAM_BOOL, 'is selected for current studyitem',VALUE_OPTIONAL),
 | 
			
		||||
            "pendingsubmission" =>  new \external_value(PARAM_BOOL, 'is selected for current studyitem', VALUE_OPTIONAL),
 | 
			
		||||
            "required" =>  new \external_value(PARAM_BOOL, 'is required for current studyitem'),
 | 
			
		||||
            "selected" =>  new \external_value(PARAM_BOOL, 'is selected for current studyitem'),
 | 
			
		||||
        ], 'referenced course information',$value);
 | 
			
		||||
        ], 'referenced course information', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $grade = $this->gradeitem->get_final($userid);
 | 
			
		||||
        // convert scale grades to corresponding scale name
 | 
			
		||||
        if(!empty($grade)){
 | 
			
		||||
            if(!is_numeric($grade->finalgrade) && empty($grade->finalgrade)){
 | 
			
		||||
        // convert scale grades to corresponding scale name.
 | 
			
		||||
        if (!empty($grade)) {
 | 
			
		||||
            if (!is_numeric($grade->finalgrade) && empty($grade->finalgrade)) {
 | 
			
		||||
                $finalgrade = "-";
 | 
			
		||||
            }
 | 
			
		||||
            else if(isset($this->scale)){
 | 
			
		||||
            else if (isset($this->scale)) {
 | 
			
		||||
                $finalgrade = $this->scale->get_nearest_item($grade->finalgrade);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                $finalgrade = round($grade->finalgrade,1);
 | 
			
		||||
                $finalgrade = round($grade->finalgrade, 1);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
| 
						 | 
				
			
			@ -242,12 +260,12 @@ class gradeinfo {
 | 
			
		|||
            $finalgrade = "-";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // retrieve the aggregator and determine completion
 | 
			
		||||
        if(!isset($this->studyitem)){
 | 
			
		||||
        // retrieve the aggregator and determine completion.
 | 
			
		||||
        if (!isset($this->studyitem)) {
 | 
			
		||||
            throw new \UnexpectedValueException("Study item not set (null) for gradeinfo in report mode");
 | 
			
		||||
        }
 | 
			
		||||
        $aggregator = $this->studyitem->studyline()->studyplan()->aggregator();
 | 
			
		||||
        $completion = $aggregator->grade_completion($this,$userid);
 | 
			
		||||
        $completion = $aggregator->grade_completion($this, $userid);
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
            "id" => $this->id,
 | 
			
		||||
| 
						 | 
				
			
			@ -268,7 +286,7 @@ class gradeinfo {
 | 
			
		|||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_model(){
 | 
			
		||||
    public function export_model() {
 | 
			
		||||
        return [
 | 
			
		||||
            "name" => $this->name,
 | 
			
		||||
            "type" => $this->gradeitem->itemmodule,
 | 
			
		||||
| 
						 | 
				
			
			@ -276,98 +294,93 @@ class gradeinfo {
 | 
			
		|||
            "required" =>  $this->is_required(),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
    public static function import(studyitem $item,array $model){
 | 
			
		||||
        if($item->type() == studyitem::COURSE){
 | 
			
		||||
    public static function import(studyitem $item, array $model) {
 | 
			
		||||
        if ($item->type() == studyitem::COURSE) {
 | 
			
		||||
            $course_id = $item->courseid();
 | 
			
		||||
            $gradeitems= grade_item::fetch_all(['itemtype' => 'mod', 'courseid' => $course_id]);
 | 
			
		||||
            foreach($gradeitems as $gi){
 | 
			
		||||
            foreach ($gradeitems as $gi) {
 | 
			
		||||
                $gi_name =  empty($outcome)?$gi->itemname:$outcome->name;
 | 
			
		||||
                $gi_type = $gi->itemmodule;
 | 
			
		||||
 | 
			
		||||
                if($gi_name == $model["name"] && $gi_type == $model["type"]){
 | 
			
		||||
                    // we have a match
 | 
			
		||||
                    if(!isset($model["selected"])){ $model["selected"] = true;}
 | 
			
		||||
                    if(!isset($model["required"])){ $model["required"] = false;}
 | 
			
		||||
                    if($model["selected"] || $model["required"]){
 | 
			
		||||
                        static::include_grade($gi->id,$item->id(),$model["selected"], $model["required"]);
 | 
			
		||||
                if ($gi_name == $model["name"] && $gi_type == $model["type"]) {
 | 
			
		||||
                    // we have a match.
 | 
			
		||||
                    if (!isset($model["selected"])) { $model["selected"] = true;}
 | 
			
		||||
                    if (!isset($model["required"])) { $model["required"] = false;}
 | 
			
		||||
                    if ($model["selected"] || $model["required"]) {
 | 
			
		||||
                        static::include_grade($gi->id, $item->id(), $model["selected"], $model["required"]);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list_course_gradables($course,studyitem $studyitem=null) {
 | 
			
		||||
    public static function list_course_gradables($course, studyitem $studyitem=null) {
 | 
			
		||||
        $list = [];
 | 
			
		||||
 | 
			
		||||
        if(method_exists("\course_modinfo","get_array_of_activities")){
 | 
			
		||||
        if (method_exists("\course_modinfo", "get_array_of_activities")) {
 | 
			
		||||
            $activities = \course_modinfo::get_array_of_activities($course);
 | 
			
		||||
        } else {
 | 
			
		||||
            // Deprecated in Moodle 4.0+, but not yet available in Moodle 3.11
 | 
			
		||||
            // Deprecated in Moodle 4.0+, but not yet available in Moodle 3.11.
 | 
			
		||||
            $activities = get_array_of_activities($course->id);
 | 
			
		||||
        }
 | 
			
		||||
        foreach($activities as $act)
 | 
			
		||||
        {
 | 
			
		||||
            if($act->visible)
 | 
			
		||||
            {
 | 
			
		||||
        foreach ($activities as $act) {
 | 
			
		||||
            if ($act->visible) {
 | 
			
		||||
                $gradeitems= grade_item::fetch_all(['itemtype' => 'mod', 'itemmodule' => $act->mod, 'iteminstance' => $act->id, 'courseid' => $course->id]);
 | 
			
		||||
                if(!empty($gradeitems))
 | 
			
		||||
                {
 | 
			
		||||
                    foreach($gradeitems as $gi){
 | 
			
		||||
                        if(($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE))
 | 
			
		||||
                        {
 | 
			
		||||
                if (!empty($gradeitems)) {
 | 
			
		||||
                    foreach ($gradeitems as $gi) {
 | 
			
		||||
                        if (($gi->gradetype == GRADE_TYPE_VALUE || $gi->gradetype == GRADE_TYPE_SCALE)) {
 | 
			
		||||
                            try {
 | 
			
		||||
                                $gradable = new static($gi->id,$studyitem);
 | 
			
		||||
                                $gradable = new static($gi->id, $studyitem);
 | 
			
		||||
                                $list[] = $gradable;
 | 
			
		||||
                            }
 | 
			
		||||
                            catch(\InvalidArgumentException $x){}
 | 
			
		||||
                            catch(\InvalidArgumentException $x) {}
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        usort($list, function($a,$b){
 | 
			
		||||
        usort($list, function($a, $b) {
 | 
			
		||||
            $course = $a->coursesort <=> $b->coursesort;
 | 
			
		||||
            return ($course != 0)?$course:$a->gradeitem->sortorder <=> $b->gradeitem->sortorder;
 | 
			
		||||
        });
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list_studyitem_gradables(studyitem $studyitem)
 | 
			
		||||
    {
 | 
			
		||||
    public static function list_studyitem_gradables(studyitem $studyitem) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $table = 'local_treestudyplan_gradeinc';
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $records = $DB->get_records($table,['studyitem_id' => $studyitem->id()]);
 | 
			
		||||
        foreach($records as $r){
 | 
			
		||||
            if(isset($r->grade_item_id)){
 | 
			
		||||
        $records = $DB->get_records($table, ['studyitem_id' => $studyitem->id()]);
 | 
			
		||||
        foreach ($records as $r) {
 | 
			
		||||
            if (isset($r->grade_item_id)) {
 | 
			
		||||
                try {
 | 
			
		||||
                    if($r->include || $r->required){
 | 
			
		||||
                        $list[] = new static($r->grade_item_id,$studyitem);
 | 
			
		||||
                    if ($r->include || $r->required) {
 | 
			
		||||
                        $list[] = new static($r->grade_item_id, $studyitem);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch(\InvalidArgumentException $x){
 | 
			
		||||
                    // on InvalidArgumentException, the grade_item id can no longer be found
 | 
			
		||||
                    // Remove the link to avoid database record hogging
 | 
			
		||||
                catch(\InvalidArgumentException $x) {
 | 
			
		||||
                    // on InvalidArgumentException, the grade_item id can no longer be found.
 | 
			
		||||
                    // Remove the link to avoid database record hogging.
 | 
			
		||||
                    $DB->delete_records($table, ['id' => $r->id]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        usort($list, function($a,$b){
 | 
			
		||||
        usort($list, function($a, $b) {
 | 
			
		||||
            $course = $a->coursesort <=> $b->coursesort;
 | 
			
		||||
            return ($course != 0)?$course:$a->gradeitem->sortorder <=> $b->gradeitem->sortorder;
 | 
			
		||||
        });
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function include_grade(int $grade_id,int $item_id,bool $include,bool $required=false) {
 | 
			
		||||
    public static function include_grade(int $grade_id, int $item_id, bool $include, bool $required=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $table = 'local_treestudyplan_gradeinc';
 | 
			
		||||
        if($include){
 | 
			
		||||
            // make sure a record exits
 | 
			
		||||
            $r = $DB->get_record($table,['studyitem_id' => $item_id, 'grade_item_id' => $grade_id]);
 | 
			
		||||
            if($r){
 | 
			
		||||
        if ($include) {
 | 
			
		||||
            // make sure a record exits.
 | 
			
		||||
            $r = $DB->get_record($table, ['studyitem_id' => $item_id, 'grade_item_id' => $grade_id]);
 | 
			
		||||
            if ($r) {
 | 
			
		||||
                $r->include = 1;
 | 
			
		||||
                $r->required = boolval($required)?1:0;
 | 
			
		||||
                $id = $DB->update_record($table, $r);
 | 
			
		||||
| 
						 | 
				
			
			@ -380,9 +393,9 @@ class gradeinfo {
 | 
			
		|||
                );
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // remove if it should not be included
 | 
			
		||||
            $r = $DB->get_record($table,['studyitem_id' => $item_id, 'grade_item_id' => $grade_id]);
 | 
			
		||||
            if($r){
 | 
			
		||||
            // remove if it should not be included.
 | 
			
		||||
            $r = $DB->get_record($table, ['studyitem_id' => $item_id, 'grade_item_id' => $grade_id]);
 | 
			
		||||
            if ($r) {
 | 
			
		||||
                $DB->delete_records($table, ['id' => $r->id]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,33 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
use \grade_item;
 | 
			
		||||
 | 
			
		||||
// $gi->courseid, 
 | 
			
		||||
// $gi->itemmodule, 
 | 
			
		||||
// $gi->iteminstance
 | 
			
		||||
// $gi->courseid, .
 | 
			
		||||
// $gi->itemmodule, .
 | 
			
		||||
// $gi->iteminstance.
 | 
			
		||||
class gradingscanner
 | 
			
		||||
{
 | 
			
		||||
    private static $mod_supported = [];
 | 
			
		||||
| 
						 | 
				
			
			@ -16,19 +36,19 @@ class gradingscanner
 | 
			
		|||
    private $gi = null;
 | 
			
		||||
    private $pending_cache = [];
 | 
			
		||||
 | 
			
		||||
    public static function supported($mod){
 | 
			
		||||
        if(!array_key_exists($mod,self::$mod_supported)){
 | 
			
		||||
    public static function supported($mod) {
 | 
			
		||||
        if (!array_key_exists($mod, self::$mod_supported)) {
 | 
			
		||||
            self::$mod_supported[$mod] = class_exists("\local_treestudyplan\\local\\ungradedscanners\\{$mod}_scanner");
 | 
			
		||||
        }
 | 
			
		||||
        return self::$mod_supported[$mod];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function get_course_students($courseid){
 | 
			
		||||
    public static function get_course_students($courseid) {
 | 
			
		||||
        global $CFG;
 | 
			
		||||
        if(!array_key_exists($courseid,self::$course_students)){
 | 
			
		||||
        if (!array_key_exists($courseid, self::$course_students)) {
 | 
			
		||||
            $students = [];
 | 
			
		||||
            $context = \context_course::instance($courseid);
 | 
			
		||||
            foreach (explode(',', $CFG->gradebookroles) as $roleid) {
 | 
			
		||||
            foreach (explode(', ', $CFG->gradebookroles) as $roleid) {
 | 
			
		||||
                $roleid = trim($roleid);
 | 
			
		||||
                $students = array_keys(get_role_users($roleid, $context, false, 'u.id', 'u.id ASC'));
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -37,22 +57,22 @@ class gradingscanner
 | 
			
		|||
        return self::$course_students[$courseid];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct(grade_item $gi){
 | 
			
		||||
    public function __construct(grade_item $gi) {
 | 
			
		||||
        $this->courseid = $gi->courseid;
 | 
			
		||||
        $this->gi = $gi;
 | 
			
		||||
        if(self::supported($gi->itemmodule)) {
 | 
			
		||||
        if (self::supported($gi->itemmodule)) {
 | 
			
		||||
            $scannerclass = "\local_treestudyplan\\local\ungradedscanners\\{$gi->itemmodule}_scanner";
 | 
			
		||||
            $this->scanner = new $scannerclass($gi);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function is_available(){
 | 
			
		||||
    public function is_available() {
 | 
			
		||||
        return $this->scanner !== null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function pending($userid){
 | 
			
		||||
        if(!array_key_exists($userid, $this->pending_cache)){
 | 
			
		||||
            if($this->scanner === null) {
 | 
			
		||||
    public function pending($userid) {
 | 
			
		||||
        if (!array_key_exists($userid, $this->pending_cache)) {
 | 
			
		||||
            if ($this->scanner === null) {
 | 
			
		||||
                $this->pending_cache[$userid] = false;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,36 +82,36 @@ class gradingscanner
 | 
			
		|||
        return $this->pending_cache[$userid];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure($value=VALUE_OPTIONAL){
 | 
			
		||||
    public static function structure($value=VALUE_OPTIONAL) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "ungraded" => new \external_value(PARAM_INT, 'number of ungraded submissions'),
 | 
			
		||||
            "completed" => new \external_value(PARAM_INT, 'number of completed students'),
 | 
			
		||||
            "completed_pass" => new \external_value(PARAM_INT, 'number of completed-pass students'),
 | 
			
		||||
            "completed_fail" => new \external_value(PARAM_INT, 'number of completed-fail students'),
 | 
			
		||||
            "students" => new \external_value(PARAM_INT, 'number of students that should submit'),
 | 
			
		||||
        ],"details about gradable submissions",$value);        
 | 
			
		||||
        ], "details about gradable submissions", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function model(){
 | 
			
		||||
        // Upda
 | 
			
		||||
    public function model() {
 | 
			
		||||
        // Upda.
 | 
			
		||||
        $students = self::get_course_students($this->courseid);
 | 
			
		||||
        $completed = 0;
 | 
			
		||||
        $ungraded = 0;
 | 
			
		||||
        $completed_pass = 0;
 | 
			
		||||
        $completed_fail = 0;
 | 
			
		||||
        foreach($students as $userid){
 | 
			
		||||
            if($this->pending($userid)){
 | 
			
		||||
                // First check if the completion needs grading
 | 
			
		||||
        foreach ($students as $userid) {
 | 
			
		||||
            if ($this->pending($userid)) {
 | 
			
		||||
                // First check if the completion needs grading.
 | 
			
		||||
                $ungraded++;
 | 
			
		||||
            } else {
 | 
			
		||||
                $grade = $this->gi->get_final($userid);
 | 
			
		||||
                if(!is_numeric($grade->finalgrade) && empty($grade->finalgrade)){
 | 
			
		||||
                    //skip
 | 
			
		||||
                if (!is_numeric($grade->finalgrade) && empty($grade->finalgrade)) {
 | 
			
		||||
                    //skip.
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //compare grade to minimum grade
 | 
			
		||||
                    if($this->grade_passed($grade)){
 | 
			
		||||
                    //compare grade to minimum grade.
 | 
			
		||||
                    if ($this->grade_passed($grade)) {
 | 
			
		||||
                        $completed_pass++;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $completed_fail++;
 | 
			
		||||
| 
						 | 
				
			
			@ -110,43 +130,40 @@ class gradingscanner
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Function copied from bistate aggregator to avoid reference mazes
 | 
			
		||||
    private function grade_passed($grade){
 | 
			
		||||
    // Function copied from bistate aggregator to avoid reference mazes.
 | 
			
		||||
    private function grade_passed($grade) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $table = "local_treestudyplan_gradecfg";
 | 
			
		||||
        // first determine if we have a grade_config for this scale or this maximum grade
 | 
			
		||||
        // first determine if we have a grade_config for this scale or this maximum grade.
 | 
			
		||||
        $finalgrade = $grade->finalgrade;
 | 
			
		||||
        $scale = $this->gi->load_scale();
 | 
			
		||||
        if( isset($scale)){
 | 
			
		||||
            $gradecfg = $DB->get_record($table,["scale_id"=>$scale->id]);
 | 
			
		||||
        if ( isset($scale)) {
 | 
			
		||||
            $gradecfg = $DB->get_record($table, ["scale_id"=>$scale->id]);
 | 
			
		||||
        }
 | 
			
		||||
        else if($this->gi->grademin == 0)
 | 
			
		||||
        {
 | 
			
		||||
            $gradecfg = $DB->get_record($table,["grade_points"=>$this->gi->grademax]);
 | 
			
		||||
        else if ($this->gi->grademin == 0) {
 | 
			
		||||
            $gradecfg = $DB->get_record($table, ["grade_points"=>$this->gi->grademax]);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            $gradecfg = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // for point grades, a provided grade pass overrides the defaults in the gradeconfig
 | 
			
		||||
        // for scales, the configuration in the gradeconfig is leading
 | 
			
		||||
        // for point grades, a provided grade pass overrides the defaults in the gradeconfig.
 | 
			
		||||
        // for scales, the configuration in the gradeconfig is leading.
 | 
			
		||||
 | 
			
		||||
        if($gradecfg && (isset($scale) || $this->gi->gradepass == 0))
 | 
			
		||||
        {
 | 
			
		||||
            // if so, we need to know if the grade is 
 | 
			
		||||
            if($finalgrade >= $gradecfg->min_completed){
 | 
			
		||||
        if ($gradecfg && (isset($scale) || $this->gi->gradepass == 0)) {
 | 
			
		||||
            // if so, we need to know if the grade is .
 | 
			
		||||
            if ($finalgrade >= $gradecfg->min_completed) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if($this->gi->gradepass > 0)
 | 
			
		||||
        {
 | 
			
		||||
        else if ($this->gi->gradepass > 0) {
 | 
			
		||||
            $range = floatval($this->gi->grademax - $this->gi->grademin);
 | 
			
		||||
            // if no gradeconfig and gradepass is set, use that one to determine config.
 | 
			
		||||
            if($finalgrade >= $this->gi->gradepass){
 | 
			
		||||
            if ($finalgrade >= $this->gi->gradepass) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			@ -154,14 +171,14 @@ class gradingscanner
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // Blind assumptions if nothing is provided
 | 
			
		||||
            // over 55% of range is completed
 | 
			
		||||
            // if range >= 3 and failed is enabled, assume that this means failed
 | 
			
		||||
            // Blind assumptions if nothing is provided.
 | 
			
		||||
            // over 55% of range is completed.
 | 
			
		||||
            // if range >= 3 and failed is enabled, assume that this means failed.
 | 
			
		||||
            $g = floatval($finalgrade - $this->gi->grademin);
 | 
			
		||||
            $range = floatval($this->gi->grademax - $this->gi->grademin);
 | 
			
		||||
            $score = $g / $range;
 | 
			
		||||
 | 
			
		||||
            if($score > 0.55){
 | 
			
		||||
            if ($score > 0.55) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\aggregators;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,48 +33,46 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
    public const DEPRECATED = false;
 | 
			
		||||
    private const DEFAULT_CONDITION = "50";
 | 
			
		||||
 | 
			
		||||
    private $thresh_excellent = 1.0;            // Minimum fraction that must be completed to aggregate as excellent (usually 1.0)
 | 
			
		||||
    private $thresh_good = 0.8;                 // Minimum fraction that must be completed to aggregate as good
 | 
			
		||||
    private $thresh_completed = 0.66;           // Minimum fraction that must be completed to aggregate as completed
 | 
			
		||||
    private $use_failed = True;                 // Support failed completion yes/no
 | 
			
		||||
    private $thresh_progress = 0.33;              // Minimum fraction that must be failed to aggregate as failed instead of progress
 | 
			
		||||
    private $accept_pending_as_submitted = False;  // Also count ungraded but submitted 
 | 
			
		||||
    private $thresh_excellent = 1.0;            // Minimum fraction that must be completed to aggregate as excellent (usually 1.0).
 | 
			
		||||
    private $thresh_good = 0.8;                 // Minimum fraction that must be completed to aggregate as good.
 | 
			
		||||
    private $thresh_completed = 0.66;           // Minimum fraction that must be completed to aggregate as completed.
 | 
			
		||||
    private $use_failed = True;                 // Support failed completion yes/no.
 | 
			
		||||
    private $thresh_progress = 0.33;              // Minimum fraction that must be failed to aggregate as failed instead of progress.
 | 
			
		||||
    private $accept_pending_as_submitted = False;  // Also count ungraded but submitted .
 | 
			
		||||
 | 
			
		||||
    public function __construct($configstr) {
 | 
			
		||||
        // allow public constructor for testing purposes
 | 
			
		||||
        // allow public constructor for testing purposes.
 | 
			
		||||
        $this->initialize($configstr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function initialize($configstr) {
 | 
			
		||||
        // First initialize with the defaults
 | 
			
		||||
        // First initialize with the defaults.
 | 
			
		||||
 | 
			
		||||
        foreach(["thresh_excellent", "thresh_good", "thresh_completed","thresh_progress",] as $key){
 | 
			
		||||
        foreach (["thresh_excellent", "thresh_good", "thresh_completed", "thresh_progress", ] as $key) {
 | 
			
		||||
            $val = intval(get_config('local_treestudyplan', "bistate_{$key}"));
 | 
			
		||||
            if($val >= 0 && $val <= 100)
 | 
			
		||||
            {
 | 
			
		||||
            if ($val >= 0 && $val <= 100) {
 | 
			
		||||
                $this->$key = floatval($val)/100;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        foreach(["use_failed", "accept_pending_as_submitted"] as $key){
 | 
			
		||||
        foreach (["use_failed", "accept_pending_as_submitted"] as $key) {
 | 
			
		||||
            $this->$key = boolval(get_config('local_treestudyplan', "bistate_{$key}"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Next, decode json
 | 
			
		||||
        $config = \json_decode($configstr,true);
 | 
			
		||||
        // Next, decode json.
 | 
			
		||||
        $config = \json_decode($configstr, true);
 | 
			
		||||
 | 
			
		||||
        if(is_array($config)){
 | 
			
		||||
            // copy all valid config settings to this item
 | 
			
		||||
            foreach(["thresh_excellent", "thresh_good", "thresh_completed","thresh_progress",] as $key){
 | 
			
		||||
                if(array_key_exists($key,$config)){
 | 
			
		||||
        if (is_array($config)) {
 | 
			
		||||
            // copy all valid config settings to this item.
 | 
			
		||||
            foreach (["thresh_excellent", "thresh_good", "thresh_completed", "thresh_progress", ] as $key) {
 | 
			
		||||
                if (array_key_exists($key, $config)) {
 | 
			
		||||
                    $val = $config[$key];
 | 
			
		||||
                    if($val >= 0 && $val <= 100)
 | 
			
		||||
                    {
 | 
			
		||||
                    if ($val >= 0 && $val <= 100) {
 | 
			
		||||
                        $this->$key = floatval($val)/100;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            foreach(["use_failed", "accept_pending_as_submitted"] as $key){
 | 
			
		||||
                if(array_key_exists($key,$config)){
 | 
			
		||||
            foreach (["use_failed", "accept_pending_as_submitted"] as $key) {
 | 
			
		||||
                if (array_key_exists($key, $config)) {
 | 
			
		||||
                    $this->$key = boolval($config[$key]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +81,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Return active configuration model
 | 
			
		||||
    // Return active configuration model.
 | 
			
		||||
    public function config_string() {
 | 
			
		||||
        return json_encode([
 | 
			
		||||
            "thresh_excellent" => 100*$this->thresh_excellent,
 | 
			
		||||
| 
						 | 
				
			
			@ -74,23 +93,23 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function needSelectGradables(){ return True;}
 | 
			
		||||
    public function needSelectGradables() { return True;}
 | 
			
		||||
    public function isDeprecated() { return self::DEPRECATED;}
 | 
			
		||||
    public function useRequiredGrades() { return True;}
 | 
			
		||||
    public function useItemConditions() { return False;}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function aggregate_binary_goals(array $completions, array $required = []){
 | 
			
		||||
        // function is public to allow access for the testing code
 | 
			
		||||
    public function aggregate_binary_goals(array $completions, array $required = []) {
 | 
			
		||||
        // function is public to allow access for the testing code.
 | 
			
		||||
 | 
			
		||||
        // return te following conditions
 | 
			
		||||
        // Possible states:
 | 
			
		||||
        // - completion::EXCELLENT  - At least $thresh_excellent fraction of goals are complete and all required goals are met
 | 
			
		||||
        // - completion::GOOD       - At least $thresh_good fraction of goals are complete and all required goals are met
 | 
			
		||||
        // - completion::COMPLETED  - At least $thresh_complete fraction of goals are completed and all required goals are met
 | 
			
		||||
        // - completion::FAILED     - At least $thresh_progress fraction of goals is not failed
 | 
			
		||||
        // - completion::INCOMPLETE - No goals have been started
 | 
			
		||||
        // - completion::PROGRESS   - All other states
 | 
			
		||||
        // return te following conditions.
 | 
			
		||||
        // Possible states:.
 | 
			
		||||
        // - completion::EXCELLENT  - At least $thresh_excellent fraction of goals are complete and all required goals are met.
 | 
			
		||||
        // - completion::GOOD       - At least $thresh_good fraction of goals are complete and all required goals are met.
 | 
			
		||||
        // - completion::COMPLETED  - At least $thresh_complete fraction of goals are completed and all required goals are met.
 | 
			
		||||
        // - completion::FAILED     - At least $thresh_progress fraction of goals is not failed.
 | 
			
		||||
        // - completion::INCOMPLETE - No goals have been started.
 | 
			
		||||
        // - completion::PROGRESS   - All other states.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $total = count($completions);
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +123,7 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
 | 
			
		||||
        $MIN_PROGRESS = ($this->accept_pending_as_submitted)?completion::PENDING:completion::PROGRESS;
 | 
			
		||||
 | 
			
		||||
        foreach($completions as $index => $c) {
 | 
			
		||||
        foreach ($completions as $index => $c) {
 | 
			
		||||
 | 
			
		||||
            $completed += ($c >= completion::COMPLETED)?1:0;
 | 
			
		||||
            $progress  += ($c >= $MIN_PROGRESS)?1:0;
 | 
			
		||||
| 
						 | 
				
			
			@ -118,19 +137,19 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        $fraction_failed    = ($total >0)?(floatval($failed)/floatval($total)):0.0;
 | 
			
		||||
        $fraction_started   = ($total >0)?(floatval($started)/floatval($total)):0.0;
 | 
			
		||||
 | 
			
		||||
        if($total == 0){
 | 
			
		||||
        if ($total == 0) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        if($fraction_completed >= $this->thresh_excellent && $allrequiredmet){
 | 
			
		||||
        if ($fraction_completed >= $this->thresh_excellent && $allrequiredmet) {
 | 
			
		||||
            return completion::EXCELLENT;
 | 
			
		||||
        }
 | 
			
		||||
        else if($fraction_completed >= $this->thresh_good && $allrequiredmet){
 | 
			
		||||
        else if ($fraction_completed >= $this->thresh_good && $allrequiredmet) {
 | 
			
		||||
            return completion::GOOD;
 | 
			
		||||
        }
 | 
			
		||||
        else if($fraction_completed >= $this->thresh_completed && $allrequiredmet){
 | 
			
		||||
        else if ($fraction_completed >= $this->thresh_completed && $allrequiredmet) {
 | 
			
		||||
            return completion::COMPLETED;
 | 
			
		||||
        }
 | 
			
		||||
        else if($started == 0){
 | 
			
		||||
        else if ($started == 0) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -138,25 +157,25 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
 | 
			
		||||
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
 | 
			
		||||
        $course = $courseinfo->course();
 | 
			
		||||
        $coursefinished = ($course->enddate)?($course->enddate < time()):false;
 | 
			
		||||
        // Note: studyitem condition config is not used in this aggregator.
 | 
			
		||||
        // loop through all associated gradables and count the totals, completed, etc..
 | 
			
		||||
        $completions = [];
 | 
			
		||||
        $required = [];
 | 
			
		||||
        foreach(gradeinfo::list_studyitem_gradables($studyitem) as $gi){
 | 
			
		||||
            $completions[] = $this->grade_completion($gi,$userid);
 | 
			
		||||
            if($gi->is_required()){
 | 
			
		||||
                // if it's a required grade 
 | 
			
		||||
                // also add it's index in the completion list to the list of required grades 
 | 
			
		||||
        foreach (gradeinfo::list_studyitem_gradables($studyitem) as $gi) {
 | 
			
		||||
            $completions[] = $this->grade_completion($gi, $userid);
 | 
			
		||||
            if ($gi->is_required()) {
 | 
			
		||||
                // if it's a required grade .
 | 
			
		||||
                // also add it's index in the completion list to the list of required grades .
 | 
			
		||||
                $required[] = count($completions) - 1;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Combine the aquired completions into one
 | 
			
		||||
        $result =  self::aggregate_binary_goals($completions,$required);
 | 
			
		||||
        if($this->use_failed && $result == completion::PROGRESS && $coursefinished){
 | 
			
		||||
        // Combine the aquired completions into one.
 | 
			
		||||
        $result =  self::aggregate_binary_goals($completions, $required);
 | 
			
		||||
        if ($this->use_failed && $result == completion::PROGRESS && $coursefinished) {
 | 
			
		||||
            return completion::FAILED;
 | 
			
		||||
        } else {
 | 
			
		||||
            return $result;
 | 
			
		||||
| 
						 | 
				
			
			@ -164,33 +183,33 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function aggregate_junction(array $completion, studyitem $studyitem = null, $userid = 0){
 | 
			
		||||
    public function aggregate_junction(array $completion, studyitem $studyitem = null, $userid = 0) {
 | 
			
		||||
        // Aggregate multiple incoming states into one junction or finish.
 | 
			
		||||
        // Possible states:
 | 
			
		||||
        // - completion::EXCELLENT  - All incoming states are excellent
 | 
			
		||||
        // - completion::GOOD       - All incoming states are at least good
 | 
			
		||||
        // - completion::COMPLETED  - All incoming states are at least completed
 | 
			
		||||
        // - completion::FAILED     - All incoming states are failed
 | 
			
		||||
        // - completion::INCOMPLETE - All incoming states are incomplete
 | 
			
		||||
        // - completion::PROGRESS   - All other states
 | 
			
		||||
        // Possible states:.
 | 
			
		||||
        // - completion::EXCELLENT  - All incoming states are excellent.
 | 
			
		||||
        // - completion::GOOD       - All incoming states are at least good.
 | 
			
		||||
        // - completion::COMPLETED  - All incoming states are at least completed.
 | 
			
		||||
        // - completion::FAILED     - All incoming states are failed.
 | 
			
		||||
        // - completion::INCOMPLETE - All incoming states are incomplete.
 | 
			
		||||
        // - completion::PROGRESS   - All other states.
 | 
			
		||||
 | 
			
		||||
        // First count all states
 | 
			
		||||
        // First count all states.
 | 
			
		||||
        $statecount = completion::count_states($completion);
 | 
			
		||||
        $total = count($completion);
 | 
			
		||||
 | 
			
		||||
        if( $total == $statecount[completion::EXCELLENT]){
 | 
			
		||||
        if ( $total == $statecount[completion::EXCELLENT]) {
 | 
			
		||||
            return completion::EXCELLENT;
 | 
			
		||||
        }
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD]){
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD]) {
 | 
			
		||||
            return completion::GOOD;
 | 
			
		||||
        }
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD] + $statecount[completion::COMPLETED]){
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD] + $statecount[completion::COMPLETED]) {
 | 
			
		||||
            return completion::COMPLETED;
 | 
			
		||||
        }
 | 
			
		||||
        else if(    $statecount[completion::FAILED]){
 | 
			
		||||
        else if (    $statecount[completion::FAILED]) {
 | 
			
		||||
            return completion::FAILED;
 | 
			
		||||
        }
 | 
			
		||||
        else if( $total == $statecount[completion::INCOMPLETE]){
 | 
			
		||||
        else if ( $total == $statecount[completion::INCOMPLETE]) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -203,21 +222,20 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        $table = "local_treestudyplan_gradecfg";
 | 
			
		||||
        $gradeitem = $gradeinfo->getGradeitem();
 | 
			
		||||
        $grade = $gradeitem->get_final($userid);
 | 
			
		||||
        $course = \get_course($gradeitem->courseid); // Fetch course from cache
 | 
			
		||||
        $course = \get_course($gradeitem->courseid); // Fetch course from cache.
 | 
			
		||||
        $coursefinished = ($course->enddate)?($course->enddate < time()):false;
 | 
			
		||||
 | 
			
		||||
        if(empty($grade)){
 | 
			
		||||
        if (empty($grade)) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        else if($grade->finalgrade === NULL)
 | 
			
		||||
        {
 | 
			
		||||
            // on assignments, grade NULL means a submission has not yet been graded,
 | 
			
		||||
            // but on quizes this can also mean a quiz might have been started
 | 
			
		||||
            // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items
 | 
			
		||||
        else if ($grade->finalgrade === NULL) {
 | 
			
		||||
            // on assignments, grade NULL means a submission has not yet been graded,.
 | 
			
		||||
            // but on quizes this can also mean a quiz might have been started.
 | 
			
		||||
            // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items.
 | 
			
		||||
 | 
			
		||||
            // Since we want old results to be visible until a pending item was graded, we only use this state here.
 | 
			
		||||
            // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model
 | 
			
		||||
            if($gradeinfo->getGradingscanner()->pending($userid)){
 | 
			
		||||
            // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model.
 | 
			
		||||
            if ($gradeinfo->getGradingscanner()->pending($userid)) {
 | 
			
		||||
                return completion::PENDING;
 | 
			
		||||
            } else {
 | 
			
		||||
                return completion::INCOMPLETE;
 | 
			
		||||
| 
						 | 
				
			
			@ -225,49 +243,44 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            $grade = $gradeitem->get_final($userid);
 | 
			
		||||
            // first determine if we have a grade_config for this scale or this maximum grade
 | 
			
		||||
            // first determine if we have a grade_config for this scale or this maximum grade.
 | 
			
		||||
            $finalgrade = $grade->finalgrade;
 | 
			
		||||
            $scale = $gradeinfo->getScale();
 | 
			
		||||
            if( isset($scale)){
 | 
			
		||||
                $gradecfg = $DB->get_record($table,["scale_id"=>$scale->id]);
 | 
			
		||||
            if ( isset($scale)) {
 | 
			
		||||
                $gradecfg = $DB->get_record($table, ["scale_id"=>$scale->id]);
 | 
			
		||||
            }
 | 
			
		||||
            else if($gradeitem->grademin == 0)
 | 
			
		||||
            {
 | 
			
		||||
                $gradecfg = $DB->get_record($table,["grade_points"=>$gradeitem->grademax]);
 | 
			
		||||
            else if ($gradeitem->grademin == 0) {
 | 
			
		||||
                $gradecfg = $DB->get_record($table, ["grade_points"=>$gradeitem->grademax]);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                $gradecfg = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // for point grades, a provided grade pass overrides the defaults in the gradeconfig
 | 
			
		||||
            // for scales, the configuration in the gradeconfig is leading
 | 
			
		||||
            // for point grades, a provided grade pass overrides the defaults in the gradeconfig.
 | 
			
		||||
            // for scales, the configuration in the gradeconfig is leading.
 | 
			
		||||
 | 
			
		||||
            if($gradecfg && (isset($scale) || $gradeitem->gradepass == 0))
 | 
			
		||||
            {
 | 
			
		||||
                // if so, we need to know if the grade is 
 | 
			
		||||
                if($finalgrade >= $gradecfg->min_completed){
 | 
			
		||||
                    // return completed if completed
 | 
			
		||||
            if ($gradecfg && (isset($scale) || $gradeitem->gradepass == 0)) {
 | 
			
		||||
                // if so, we need to know if the grade is .
 | 
			
		||||
                if ($finalgrade >= $gradecfg->min_completed) {
 | 
			
		||||
                    // return completed if completed.
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else if($this->use_failed && $coursefinished)
 | 
			
		||||
                {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is less than the minimum grade for progress
 | 
			
		||||
                else if ($this->use_failed && $coursefinished) {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is less than the minimum grade for progress.
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    return completion::PROGRESS;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if($gradeitem->gradepass > 0)
 | 
			
		||||
            {
 | 
			
		||||
            else if ($gradeitem->gradepass > 0) {
 | 
			
		||||
                $range = floatval($gradeitem->grademax - $gradeitem->grademin);
 | 
			
		||||
                // if no gradeconfig and gradepass is set, use that one to determine config.
 | 
			
		||||
                if($finalgrade >= $gradeitem->gradepass){
 | 
			
		||||
                if ($finalgrade >= $gradeitem->gradepass) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else if($this->use_failed && $coursefinished)
 | 
			
		||||
                {
 | 
			
		||||
                else if ($this->use_failed && $coursefinished) {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -276,18 +289,17 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Blind assumptions if nothing is provided
 | 
			
		||||
                // over 55% of range is completed
 | 
			
		||||
                // if range >= 3 and failed is enabled, assume that this means failed
 | 
			
		||||
                // Blind assumptions if nothing is provided.
 | 
			
		||||
                // over 55% of range is completed.
 | 
			
		||||
                // if range >= 3 and failed is enabled, assume that this means failed.
 | 
			
		||||
                $g = floatval($finalgrade - $gradeitem->grademin);
 | 
			
		||||
                $range = floatval($gradeitem->grademax - $gradeitem->grademin);
 | 
			
		||||
                $score = $g / $range;
 | 
			
		||||
 | 
			
		||||
                if($score > 0.55){
 | 
			
		||||
                if ($score > 0.55) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else if($this->use_failed && $coursefinished)
 | 
			
		||||
                {
 | 
			
		||||
                else if ($this->use_failed && $coursefinished) {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\aggregators;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11,26 +32,26 @@ use \local_treestudyplan\debug;
 | 
			
		|||
 | 
			
		||||
class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		||||
    public const DEPRECATED = false;
 | 
			
		||||
    private $accept_pending_as_submitted = False;  // Also count ungraded but submitted 
 | 
			
		||||
    private $accept_pending_as_submitted = False;  // Also count ungraded but submitted .
 | 
			
		||||
 | 
			
		||||
    public function __construct($configstr) {
 | 
			
		||||
        // allow public constructor for testing purposes
 | 
			
		||||
        // allow public constructor for testing purposes.
 | 
			
		||||
        $this->initialize($configstr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function initialize($configstr) {
 | 
			
		||||
        // First initialize with the defaults
 | 
			
		||||
        foreach(["accept_pending_as_submitted"] as $key){
 | 
			
		||||
        // First initialize with the defaults.
 | 
			
		||||
        foreach (["accept_pending_as_submitted"] as $key) {
 | 
			
		||||
            $this->$key = boolval(get_config('local_treestudyplan', "bistate_{$key}"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Next, decode json
 | 
			
		||||
        $config = \json_decode($configstr,true);
 | 
			
		||||
        // Next, decode json.
 | 
			
		||||
        $config = \json_decode($configstr, true);
 | 
			
		||||
 | 
			
		||||
        if(is_array($config)){
 | 
			
		||||
            // copy all valid config settings to this item
 | 
			
		||||
            foreach(["accept_pending_as_submitted"] as $key){
 | 
			
		||||
                if(array_key_exists($key,$config)){
 | 
			
		||||
        if (is_array($config)) {
 | 
			
		||||
            // copy all valid config settings to this item.
 | 
			
		||||
            foreach (["accept_pending_as_submitted"] as $key) {
 | 
			
		||||
                if (array_key_exists($key, $config)) {
 | 
			
		||||
                    $this->$key = boolval($config[$key]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -39,14 +60,14 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Return active configuration model
 | 
			
		||||
    // Return active configuration model.
 | 
			
		||||
    public function config_string() {
 | 
			
		||||
        return json_encode([
 | 
			
		||||
            "accept_pending_as_submitted" => $this->accept_pending_as_submitted,
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function needSelectGradables(){ return False;}
 | 
			
		||||
    public function needSelectGradables() { return False;}
 | 
			
		||||
    public function isDeprecated() { return self::DEPRECATED;}
 | 
			
		||||
    public function useRequiredGrades() { return True;}
 | 
			
		||||
    public function useItemConditions() { return False;}
 | 
			
		||||
| 
						 | 
				
			
			@ -68,37 +89,37 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
     * @return void
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
 | 
			
		||||
         // Retrieve the core completion info from the core
 | 
			
		||||
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
 | 
			
		||||
         // Retrieve the core completion info from the core.
 | 
			
		||||
         $course = $courseinfo->course();
 | 
			
		||||
         $completion = new \completion_info($course);
 | 
			
		||||
 | 
			
		||||
         if ($completion->is_enabled() && $completion->is_tracked_user($userid)){
 | 
			
		||||
            if($completion->is_course_complete($userid)){
 | 
			
		||||
         if ($completion->is_enabled() && $completion->is_tracked_user($userid)) {
 | 
			
		||||
            if ($completion->is_course_complete($userid)) {
 | 
			
		||||
                // Now, the trick is to determine what constitutes excellent and good completion....
 | 
			
		||||
                // TODO: Determine excellent and maybe good completion
 | 
			
		||||
                // TODO: Determine excellent and maybe good completion.
 | 
			
		||||
                // Option: Use course end grade to determine that...
 | 
			
		||||
                // Probably needs a config value in the aggregator....
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                return completion::COMPLETED;
 | 
			
		||||
            } else {
 | 
			
		||||
                // Check if the course is over or not, if it is over, display failed
 | 
			
		||||
                // Else, return PROGRESS
 | 
			
		||||
                // Check if the course is over or not, if it is over, display failed.
 | 
			
		||||
                // Else, return PROGRESS.
 | 
			
		||||
 | 
			
		||||
                // Retrieve timing through courseinfo 
 | 
			
		||||
                // Retrieve timing through courseinfo .
 | 
			
		||||
                $timing = courseinfo::coursetiming($course);
 | 
			
		||||
 | 
			
		||||
                // Not met and time is passed, means FAILED
 | 
			
		||||
                if($timing == "past"){
 | 
			
		||||
                // Not met and time is passed, means FAILED.
 | 
			
		||||
                if ($timing == "past") {
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    // Check if any of the requirements are being met?
 | 
			
		||||
                    // Check if any of the requirements are being met?.
 | 
			
		||||
                    $completions = $completion->get_completions($userid);
 | 
			
		||||
                    foreach($completions as $c){
 | 
			
		||||
                        if($c->is_complete()){
 | 
			
		||||
                            // If so, return progress
 | 
			
		||||
                    foreach ($completions as $c) {
 | 
			
		||||
                        if ($c->is_complete()) {
 | 
			
		||||
                            // If so, return progress.
 | 
			
		||||
                            return completion::PROGRESS;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			@ -111,33 +132,33 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function aggregate_junction(array $completion, studyitem $studyitem = null, $userid = 0){
 | 
			
		||||
    public function aggregate_junction(array $completion, studyitem $studyitem = null, $userid = 0) {
 | 
			
		||||
        // Aggregate multiple incoming states into one junction or finish.
 | 
			
		||||
        // Possible states:
 | 
			
		||||
        // - completion::EXCELLENT  - All incoming states are excellent
 | 
			
		||||
        // - completion::GOOD       - All incoming states are at least good
 | 
			
		||||
        // - completion::COMPLETED  - All incoming states are at least completed
 | 
			
		||||
        // - completion::FAILED     - All incoming states are failed
 | 
			
		||||
        // - completion::INCOMPLETE - All incoming states are incomplete
 | 
			
		||||
        // - completion::PROGRESS   - All other states
 | 
			
		||||
        // Possible states:.
 | 
			
		||||
        // - completion::EXCELLENT  - All incoming states are excellent.
 | 
			
		||||
        // - completion::GOOD       - All incoming states are at least good.
 | 
			
		||||
        // - completion::COMPLETED  - All incoming states are at least completed.
 | 
			
		||||
        // - completion::FAILED     - All incoming states are failed.
 | 
			
		||||
        // - completion::INCOMPLETE - All incoming states are incomplete.
 | 
			
		||||
        // - completion::PROGRESS   - All other states.
 | 
			
		||||
 | 
			
		||||
        // First count all states
 | 
			
		||||
        // First count all states.
 | 
			
		||||
        $statecount = completion::count_states($completion);
 | 
			
		||||
        $total = count($completion);
 | 
			
		||||
 | 
			
		||||
        if( $total == $statecount[completion::EXCELLENT]){
 | 
			
		||||
        if ( $total == $statecount[completion::EXCELLENT]) {
 | 
			
		||||
            return completion::EXCELLENT;
 | 
			
		||||
        }
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD]){
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD]) {
 | 
			
		||||
            return completion::GOOD;
 | 
			
		||||
        }
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD] + $statecount[completion::COMPLETED]){
 | 
			
		||||
        else if ( $total == $statecount[completion::EXCELLENT] + $statecount[completion::GOOD] + $statecount[completion::COMPLETED]) {
 | 
			
		||||
            return completion::COMPLETED;
 | 
			
		||||
        }
 | 
			
		||||
        else if(    $statecount[completion::FAILED]){
 | 
			
		||||
        else if (    $statecount[completion::FAILED]) {
 | 
			
		||||
            return completion::FAILED;
 | 
			
		||||
        }
 | 
			
		||||
        else if( $total == $statecount[completion::INCOMPLETE]){
 | 
			
		||||
        else if ( $total == $statecount[completion::INCOMPLETE]) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,7 +167,7 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // CORE COMPLETION DOESN'T REALLY USE THE FUNCTIONS BELOW
 | 
			
		||||
    // CORE COMPLETION DOESN'T REALLY USE THE FUNCTIONS BELOW.
 | 
			
		||||
    // AGGREGATORS ARE GOING TO BE DEPRECATED ANYWAY... but used in legacy parts of this plugin.
 | 
			
		||||
 | 
			
		||||
    public function grade_completion(gradeinfo $gradeinfo, $userid) {
 | 
			
		||||
| 
						 | 
				
			
			@ -155,18 +176,17 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        $gradeitem = $gradeinfo->getGradeitem();
 | 
			
		||||
        $grade = $gradeitem->get_final($userid);
 | 
			
		||||
 | 
			
		||||
        if(empty($grade)){
 | 
			
		||||
        if (empty($grade)) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        else if($grade->finalgrade === NULL)
 | 
			
		||||
        {
 | 
			
		||||
            // on assignments, grade NULL means a submission has not yet been graded,
 | 
			
		||||
            // but on quizes this can also mean a quiz might have been started
 | 
			
		||||
            // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items
 | 
			
		||||
        else if ($grade->finalgrade === NULL) {
 | 
			
		||||
            // on assignments, grade NULL means a submission has not yet been graded,.
 | 
			
		||||
            // but on quizes this can also mean a quiz might have been started.
 | 
			
		||||
            // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items.
 | 
			
		||||
 | 
			
		||||
            // Since we want old results to be visible until a pending item was graded, we only use this state here.
 | 
			
		||||
            // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model
 | 
			
		||||
            if($gradeinfo->getGradingscanner()->pending($userid)){
 | 
			
		||||
            // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model.
 | 
			
		||||
            if ($gradeinfo->getGradingscanner()->pending($userid)) {
 | 
			
		||||
                return completion::PENDING;
 | 
			
		||||
            } else {
 | 
			
		||||
                return completion::INCOMPLETE;
 | 
			
		||||
| 
						 | 
				
			
			@ -174,49 +194,44 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            $grade = $gradeitem->get_final($userid);
 | 
			
		||||
            // first determine if we have a grade_config for this scale or this maximum grade
 | 
			
		||||
            // first determine if we have a grade_config for this scale or this maximum grade.
 | 
			
		||||
            $finalgrade = $grade->finalgrade;
 | 
			
		||||
            $scale = $gradeinfo->getScale();
 | 
			
		||||
            if( isset($scale)){
 | 
			
		||||
                $gradecfg = $DB->get_record($table,["scale_id"=>$scale->id]);
 | 
			
		||||
            if ( isset($scale)) {
 | 
			
		||||
                $gradecfg = $DB->get_record($table, ["scale_id"=>$scale->id]);
 | 
			
		||||
            }
 | 
			
		||||
            else if($gradeitem->grademin == 0)
 | 
			
		||||
            {
 | 
			
		||||
                $gradecfg = $DB->get_record($table,["grade_points"=>$gradeitem->grademax]);
 | 
			
		||||
            else if ($gradeitem->grademin == 0) {
 | 
			
		||||
                $gradecfg = $DB->get_record($table, ["grade_points"=>$gradeitem->grademax]);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                $gradecfg = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // for point grades, a provided grade pass overrides the defaults in the gradeconfig
 | 
			
		||||
            // for scales, the configuration in the gradeconfig is leading
 | 
			
		||||
            // for point grades, a provided grade pass overrides the defaults in the gradeconfig.
 | 
			
		||||
            // for scales, the configuration in the gradeconfig is leading.
 | 
			
		||||
 | 
			
		||||
            if($gradecfg && (isset($scale) || $gradeitem->gradepass == 0))
 | 
			
		||||
            {
 | 
			
		||||
                // if so, we need to know if the grade is 
 | 
			
		||||
                if($finalgrade >= $gradecfg->min_completed){
 | 
			
		||||
                    // return completed if completed
 | 
			
		||||
            if ($gradecfg && (isset($scale) || $gradeitem->gradepass == 0)) {
 | 
			
		||||
                // if so, we need to know if the grade is .
 | 
			
		||||
                if ($finalgrade >= $gradecfg->min_completed) {
 | 
			
		||||
                    // return completed if completed.
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else if($this->use_failed && $finalgrade < $gradecfg->min_progress)
 | 
			
		||||
                {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is less than the minimum grade for progress
 | 
			
		||||
                else if ($this->use_failed && $finalgrade < $gradecfg->min_progress) {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is less than the minimum grade for progress.
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    return completion::PROGRESS;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else if($gradeitem->gradepass > 0)
 | 
			
		||||
            {
 | 
			
		||||
            else if ($gradeitem->gradepass > 0) {
 | 
			
		||||
                $range = floatval($gradeitem->grademax - $gradeitem->grademin);
 | 
			
		||||
                // if no gradeconfig and gradepass is set, use that one to determine config.
 | 
			
		||||
                if($finalgrade >= $gradeitem->gradepass){
 | 
			
		||||
                if ($finalgrade >= $gradeitem->gradepass) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else if($this->use_failed && $gradeitem->gradepass >= 3  && $range >= 3 && $finalgrade == 1)
 | 
			
		||||
                {
 | 
			
		||||
                else if ($this->use_failed && $gradeitem->gradepass >= 3  && $range >= 3 && $finalgrade == 1) {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -225,18 +240,17 @@ class core_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Blind assumptions if nothing is provided
 | 
			
		||||
                // over 55% of range is completed
 | 
			
		||||
                // if range >= 3 and failed is enabled, assume that this means failed
 | 
			
		||||
                // Blind assumptions if nothing is provided.
 | 
			
		||||
                // over 55% of range is completed.
 | 
			
		||||
                // if range >= 3 and failed is enabled, assume that this means failed.
 | 
			
		||||
                $g = floatval($finalgrade - $gradeitem->grademin);
 | 
			
		||||
                $range = floatval($gradeitem->grademax - $gradeitem->grademin);
 | 
			
		||||
                $score = $g / $range;
 | 
			
		||||
 | 
			
		||||
                if($score > 0.55){
 | 
			
		||||
                if ($score > 0.55) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else if($this->use_failed && $range >= 3 && $finalgrade == 1)
 | 
			
		||||
                {
 | 
			
		||||
                else if ($this->use_failed && $range >= 3 && $finalgrade == 1) {
 | 
			
		||||
                    // return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
 | 
			
		||||
                    return completion::FAILED;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\aggregators;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11,24 +32,23 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
    public const DEPRECATED = true;
 | 
			
		||||
    private const DEFAULT_CONDITION = "50";
 | 
			
		||||
 | 
			
		||||
    public function needSelectGradables(){ return True;}
 | 
			
		||||
    public function needSelectGradables() { return True;}
 | 
			
		||||
    public function isDeprecated() { return self::DEPRECATED;}
 | 
			
		||||
    public function useRequiredGrades() { return False;}
 | 
			
		||||
    public function useItemConditions() { return True;}
 | 
			
		||||
 | 
			
		||||
    protected function aggregate_completion(array $a, $condition = "50") {
 | 
			
		||||
        if(in_array($condition, ['ALL','67','50','ANY'])){
 | 
			
		||||
            // condition is one of the valid conditions
 | 
			
		||||
        if (in_array($condition, ['ALL', '67', '50', 'ANY'])) {
 | 
			
		||||
            // condition is one of the valid conditions.
 | 
			
		||||
            $c_completed = 0;
 | 
			
		||||
            $c_excellent = 0;
 | 
			
		||||
            $c_progress = 0;
 | 
			
		||||
            $c_pending = 0;
 | 
			
		||||
            $count = sizeof($a);
 | 
			
		||||
 | 
			
		||||
            if($count > 0)
 | 
			
		||||
            {
 | 
			
		||||
            if ($count > 0) {
 | 
			
		||||
 | 
			
		||||
                foreach($a as $c) {
 | 
			
		||||
                foreach ($a as $c) {
 | 
			
		||||
                    $c_progress += ($c>=completion::PROGRESS)?1:0;
 | 
			
		||||
                    $c_completed += ($c>=completion::COMPLETED)?1:0;
 | 
			
		||||
                    $c_excellent += ($c>=completion::EXCELLENT)?1:0;
 | 
			
		||||
| 
						 | 
				
			
			@ -42,17 +62,17 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
                    'ANY' => 1,
 | 
			
		||||
                    ][$condition];
 | 
			
		||||
 | 
			
		||||
                if($c_excellent >= $required) {
 | 
			
		||||
                if ($c_excellent >= $required) {
 | 
			
		||||
                    return completion::EXCELLENT;
 | 
			
		||||
                } else if ($c_completed >= $required) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Return PROGRESS if one or more completions are COMPLETED or EXCELLENT, but the aggregation margin is not met
 | 
			
		||||
                    // state PROGRESS will not carry on if aggregations are chained
 | 
			
		||||
                    if($c_progress > 0){
 | 
			
		||||
                    // Return PROGRESS if one or more completions are COMPLETED or EXCELLENT, but the aggregation margin is not met.
 | 
			
		||||
                    // state PROGRESS will not carry on if aggregations are chained.
 | 
			
		||||
                    if ($c_progress > 0) {
 | 
			
		||||
                        return completion::PROGRESS;
 | 
			
		||||
                    }
 | 
			
		||||
                    else if($c_pending > 0){
 | 
			
		||||
                    else if ($c_pending > 0) {
 | 
			
		||||
                        return completion::PENDING;
 | 
			
		||||
                    }
 | 
			
		||||
                    else {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,28 +86,28 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // indeterminable, return null
 | 
			
		||||
            // indeterminable, return null.
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid){
 | 
			
		||||
    public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
 | 
			
		||||
        $condition = $studyitem->conditions();
 | 
			
		||||
        if(empty($condition)){
 | 
			
		||||
        if (empty($condition)) {
 | 
			
		||||
            $condition = self::DEFAULT_CONDITION;
 | 
			
		||||
        }
 | 
			
		||||
        $list = [];
 | 
			
		||||
        foreach(gradeinfo::list_studyitem_gradables($studyitem) as $gi){
 | 
			
		||||
            $list[] = $this->grade_completion($gi,$userid);
 | 
			
		||||
        foreach (gradeinfo::list_studyitem_gradables($studyitem) as $gi) {
 | 
			
		||||
            $list[] = $this->grade_completion($gi, $userid);
 | 
			
		||||
        }
 | 
			
		||||
        $completion = self::aggregate_completion($list,$condition);
 | 
			
		||||
        $completion = self::aggregate_completion($list, $condition);
 | 
			
		||||
        return $completion;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function aggregate_junction(array $completion, studyitem $studyitem, $userid){
 | 
			
		||||
        $completed = self::aggregate_completion($completion,$studyitem->conditions());
 | 
			
		||||
        // if null result (conditions are unknown/null) - default to ALL
 | 
			
		||||
        return isset($completed)?$completed:(self::aggregate_completion($completion,'ALL'));
 | 
			
		||||
    public function aggregate_junction(array $completion, studyitem $studyitem, $userid) {
 | 
			
		||||
        $completed = self::aggregate_completion($completion, $studyitem->conditions());
 | 
			
		||||
        // if null result (conditions are unknown/null) - default to ALL.
 | 
			
		||||
        return isset($completed)?$completed:(self::aggregate_completion($completion, 'ALL'));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function grade_completion(gradeinfo $gradeinfo, $userid) {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,18 +115,17 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
        $gradeitem = $gradeinfo->getGradeitem();
 | 
			
		||||
        $grade = $gradeitem->get_final($userid);
 | 
			
		||||
 | 
			
		||||
        if(empty($grade)){
 | 
			
		||||
        if (empty($grade)) {
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
        else if($grade->finalgrade === NULL)
 | 
			
		||||
        {
 | 
			
		||||
            // on assignments, grade NULL means a submission has not yet been graded,
 | 
			
		||||
            // but on quizes this can also mean a quiz might have been started
 | 
			
		||||
            // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items
 | 
			
		||||
        else if ($grade->finalgrade === NULL) {
 | 
			
		||||
            // on assignments, grade NULL means a submission has not yet been graded,.
 | 
			
		||||
            // but on quizes this can also mean a quiz might have been started.
 | 
			
		||||
            // Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items.
 | 
			
		||||
 | 
			
		||||
            // Since we want old results to be visible until a pending item was graded, we only use this state here.
 | 
			
		||||
            // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model
 | 
			
		||||
            if($gradeinfo->getGradingscanner()->pending($userid)){
 | 
			
		||||
            // Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model.
 | 
			
		||||
            if ($gradeinfo->getGradingscanner()->pending($userid)) {
 | 
			
		||||
                return completion::PENDING;
 | 
			
		||||
            } else {
 | 
			
		||||
                return completion::INCOMPLETE;
 | 
			
		||||
| 
						 | 
				
			
			@ -117,14 +136,13 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
            $finalgrade = $grade->finalgrade;
 | 
			
		||||
            $scale = $gradeinfo->getScale();
 | 
			
		||||
 | 
			
		||||
            if($gradeitem->gradepass > 0)
 | 
			
		||||
            {
 | 
			
		||||
                // Base completion off of gradepass (if set)
 | 
			
		||||
                if($gradeitem->grademax > $gradeitem->gradepass && $finalgrade >= $gradeitem->grademax){
 | 
			
		||||
                    // If gradepass is configured 
 | 
			
		||||
            if ($gradeitem->gradepass > 0) {
 | 
			
		||||
                // Base completion off of gradepass (if set).
 | 
			
		||||
                if ($gradeitem->grademax > $gradeitem->gradepass && $finalgrade >= $gradeitem->grademax) {
 | 
			
		||||
                    // If gradepass is configured .
 | 
			
		||||
                    return completion::EXCELLENT;
 | 
			
		||||
                }
 | 
			
		||||
                else if($finalgrade >= $gradeitem->gradepass){
 | 
			
		||||
                else if ($finalgrade >= $gradeitem->gradepass) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
| 
						 | 
				
			
			@ -132,17 +150,17 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Blind assumptions:
 | 
			
		||||
                // over 55% of range is completed
 | 
			
		||||
                // over 85% of range is excellent
 | 
			
		||||
                // Blind assumptions:.
 | 
			
		||||
                // over 55% of range is completed.
 | 
			
		||||
                // over 85% of range is excellent.
 | 
			
		||||
                $g = floatval($finalgrade - $gradeitem->grademin);
 | 
			
		||||
                $range = floatval($gradeitem->grademax - $gradeitem->grademin);
 | 
			
		||||
                $score = $g / $range;
 | 
			
		||||
 | 
			
		||||
                if($score > 0.85){
 | 
			
		||||
                if ($score > 0.85) {
 | 
			
		||||
                    return completion::EXCELLENT;
 | 
			
		||||
                }
 | 
			
		||||
                else if($score > 0.55){
 | 
			
		||||
                else if ($score > 0.55) {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,26 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local;
 | 
			
		||||
 | 
			
		||||
use Exception;
 | 
			
		||||
| 
						 | 
				
			
			@ -64,135 +86,132 @@ class gradegenerator {
 | 
			
		|||
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    private function generatedfeedback(){
 | 
			
		||||
        if(file_exists("/usr/games/fortune")){
 | 
			
		||||
            // get a fortune if it is available
 | 
			
		||||
    private function generatedfeedback() {
 | 
			
		||||
        if (file_exists("/usr/games/fortune")) {
 | 
			
		||||
            // get a fortune if it is available.
 | 
			
		||||
            return shell_exec("/usr/games/fortune -n 160 -e disclaimer literature science pratchett wisdom education");
 | 
			
		||||
        } else {
 | 
			
		||||
            // get a random loremipsum string
 | 
			
		||||
            return self::$loremipsum[rand(0,count(self::$loremipsum)-1)];
 | 
			
		||||
            // get a random loremipsum string.
 | 
			
		||||
            return self::$loremipsum[rand(0, count(self::$loremipsum)-1)];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function __construct(){
 | 
			
		||||
    public function __construct() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addstudent(string $student){
 | 
			
		||||
        if(!array_key_exists($student,$this->table)){
 | 
			
		||||
    public function addstudent(string $student) {
 | 
			
		||||
        if (!array_key_exists($student, $this->table)) {
 | 
			
		||||
            $this->table[$student] = [
 | 
			
		||||
                "intelligence" => rand(70,100),
 | 
			
		||||
                "endurance" => rand(70,100),
 | 
			
		||||
                "intelligence" => rand(70, 100),
 | 
			
		||||
                "endurance" => rand(70, 100),
 | 
			
		||||
                "skills" => [],
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addskill(string $student, string $skill){
 | 
			
		||||
    public function addskill(string $student, string $skill) {
 | 
			
		||||
        $this->addstudent($student);
 | 
			
		||||
        if(!array_key_exists($skill,$this->table[$student]["skills"])){
 | 
			
		||||
        if (!array_key_exists($skill, $this->table[$student]["skills"])) {
 | 
			
		||||
            $int = $this->table[$student]["intelligence"];
 | 
			
		||||
            $end = $this->table[$student]["endurance"];
 | 
			
		||||
            $this->table[$student]["skills"][$skill] = [
 | 
			
		||||
                "intelligence" => min(100, $int + rand(-30,30)),
 | 
			
		||||
                "endurance" => min(100, $end + rand(-10,10)),
 | 
			
		||||
                "intelligence" => min(100, $int + rand(-30, 30)),
 | 
			
		||||
                "endurance" => min(100, $end + rand(-10, 10)),
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Below is mostly just for reference in the json file
 | 
			
		||||
    public function addUserNameInfo(string $student, $firstname,$lastname){
 | 
			
		||||
    // Below is mostly just for reference in the json file.
 | 
			
		||||
    public function addUserNameInfo(string $student, $firstname, $lastname) {
 | 
			
		||||
        $this->addstudent($student);
 | 
			
		||||
        $this->table[$student]["firstname"] = $firstname;
 | 
			
		||||
        $this->table[$student]["lastname"] = $lastname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function generateraw($student,$skill, $count )
 | 
			
		||||
    {
 | 
			
		||||
        $this->addskill($student,$skill);
 | 
			
		||||
    public function generateraw($student, $skill, $count ) {
 | 
			
		||||
        $this->addskill($student, $skill);
 | 
			
		||||
        $int = $this->table[$student]["skills"][$skill]["intelligence"];
 | 
			
		||||
        $end = $this->table[$student]["skills"][$skill]["endurance"];
 | 
			
		||||
 | 
			
		||||
        $results = [];
 | 
			
		||||
        $gaveup = false;
 | 
			
		||||
        for($i=0; $i < $count; $i++){
 | 
			
		||||
        for($i=0; $i < $count; $i++) {
 | 
			
		||||
            $r = new \stdClass;
 | 
			
		||||
            if($gaveup) {
 | 
			
		||||
            if ($gaveup) {
 | 
			
		||||
                $r->done = !$gaveup;
 | 
			
		||||
            } else {
 | 
			
		||||
                $r->done = (rand(0, $end) > 20); // Determine if the assignment was done
 | 
			
		||||
                $r->done = (rand(0, $end) > 20); // Determine if the assignment was done.
 | 
			
		||||
            }
 | 
			
		||||
            if($r->done){
 | 
			
		||||
                $score = rand(0,$int) ;
 | 
			
		||||
                $r->result = ($score > 20); // determine if the assignment was successful
 | 
			
		||||
                if(!$r->result){
 | 
			
		||||
            if ($r->done) {
 | 
			
		||||
                $score = rand(0, $int) ;
 | 
			
		||||
                $r->result = ($score > 20); // determine if the assignment was successful.
 | 
			
		||||
                if (!$r->result) {
 | 
			
		||||
                    $r->failed = !($score > 10);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                $r->result = false; // make sure a result property is always there
 | 
			
		||||
                $r->result = false; // make sure a result property is always there.
 | 
			
		||||
                $r->failed = true;
 | 
			
		||||
            }
 | 
			
		||||
            // Aways generate a little feedback
 | 
			
		||||
            // Aways generate a little feedback.
 | 
			
		||||
            $r->fb = $this->generatedfeedback();
 | 
			
		||||
            $results[] = $r;
 | 
			
		||||
 | 
			
		||||
            if(!$gaveup && $i >= 3) {
 | 
			
		||||
                // There is a slight chance the students with low endurance for this course will stop with this course's work entirely
 | 
			
		||||
                $gaveup = (rand(0,$end) < 15);
 | 
			
		||||
            if (!$gaveup && $i >= 3) {
 | 
			
		||||
                // There is a slight chance the students with low endurance for this course will stop with this course's work entirely.
 | 
			
		||||
                $gaveup = (rand(0, $end) < 15);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function generate($student,$skill, array $gradeinfos ){
 | 
			
		||||
    public function generate($student, $skill, array $gradeinfos ) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $table ="local_treestudyplan_gradecfg";
 | 
			
		||||
 | 
			
		||||
        $rlist = [];
 | 
			
		||||
        $gen = $this->generateraw($student,$skill, count($gradeinfos));
 | 
			
		||||
        $gen = $this->generateraw($student, $skill, count($gradeinfos));
 | 
			
		||||
 | 
			
		||||
        for($i=0; $i < count($gradeinfos); $i++){
 | 
			
		||||
        for($i=0; $i < count($gradeinfos); $i++) {
 | 
			
		||||
            $g = $gradeinfos[$i];
 | 
			
		||||
            $gi = $g->getGradeitem();
 | 
			
		||||
            $gr = $gen[$i];
 | 
			
		||||
 | 
			
		||||
            // First get the configured interpretation for this scale or grade
 | 
			
		||||
            // First get the configured interpretation for this scale or grade.
 | 
			
		||||
            $scale = $gi->load_scale();
 | 
			
		||||
            if( isset($scale)){
 | 
			
		||||
                $gradecfg = $DB->get_record($table,["scale_id"=>$scale->id]);
 | 
			
		||||
            if ( isset($scale)) {
 | 
			
		||||
                $gradecfg = $DB->get_record($table, ["scale_id"=>$scale->id]);
 | 
			
		||||
            }
 | 
			
		||||
            else if($gi->grademin == 0)
 | 
			
		||||
            {
 | 
			
		||||
                $gradecfg = $DB->get_record($table,["grade_points"=>$gi->grademax]);
 | 
			
		||||
            else if ($gi->grademin == 0) {
 | 
			
		||||
                $gradecfg = $DB->get_record($table, ["grade_points"=>$gi->grademax]);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                $gradecfg = null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // next generate the grade
 | 
			
		||||
            if($gradecfg)
 | 
			
		||||
            {
 | 
			
		||||
                if(!$gr->done){
 | 
			
		||||
                    // INCOMPLETE
 | 
			
		||||
                    // fair chance of teacher forgetting to set incomplete to "no evidence"
 | 
			
		||||
                    $grade = 0;// $grade =  (rand(0,100) > 15)?max(1, $gradecfg->min_progress-1):"0";
 | 
			
		||||
            // next generate the grade.
 | 
			
		||||
            if ($gradecfg) {
 | 
			
		||||
                if (!$gr->done) {
 | 
			
		||||
                    // INCOMPLETE.
 | 
			
		||||
                    // fair chance of teacher forgetting to set incomplete to "no evidence".
 | 
			
		||||
                    $grade = 0;// $grade =  (rand(0, 100) > 15)?max(1, $gradecfg->min_progress-1):"0";.
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => $grade, "fb" =>"" ];
 | 
			
		||||
                }
 | 
			
		||||
                else if(!$gr->result){
 | 
			
		||||
                else if (!$gr->result) {
 | 
			
		||||
                    $grade =  rand($gradecfg->min_progress, $gradecfg->min_completed -1 );
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => $grade, "fb" =>$gr->fb ];
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    // COMPLETED
 | 
			
		||||
                    // COMPLETED.
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => rand( $gradecfg->min_completed, $gi->grademax ), "fb" =>$gr->fb ];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $r->gradetext = $r->grade;
 | 
			
		||||
                if( isset($scale)){
 | 
			
		||||
                if ( isset($scale)) {
 | 
			
		||||
                    $scaleitems = $scale->load_items();
 | 
			
		||||
                    if($r->grade > 0){
 | 
			
		||||
                    if ($r->grade > 0) {
 | 
			
		||||
                        $r->gradetext = trim($scale->get_nearest_item($r->grade));
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $r->gradetext = "-";
 | 
			
		||||
| 
						 | 
				
			
			@ -200,41 +219,40 @@ class gradegenerator {
 | 
			
		|||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else if($gi->gradepass > 0)
 | 
			
		||||
            {
 | 
			
		||||
                if(!$gr->done){
 | 
			
		||||
                    // INCOMPLETe or FAILED
 | 
			
		||||
            else if ($gi->gradepass > 0) {
 | 
			
		||||
                if (!$gr->done) {
 | 
			
		||||
                    // INCOMPLETe or FAILED.
 | 
			
		||||
                    $grade =  rand(0, $gi->gradepass/2);
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => $grade, "fb" =>($grade > 0)?$gr->fb:"" ];
 | 
			
		||||
                }
 | 
			
		||||
                else if(!$gr->result){
 | 
			
		||||
                    //PROGRESS
 | 
			
		||||
                else if (!$gr->result) {
 | 
			
		||||
                    //PROGRESS.
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => rand( round($gi->gradepass/2), $gi->gradepass -1 ), "fb" =>$gr->fb ];
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    // COMPLETED
 | 
			
		||||
                    // COMPLETED.
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => rand(  $gi->gradepass, $gi->grademax ), "fb" =>$gr->fb ];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $r->gradetext = $r->grade;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Blind assumptions if nothing is provided
 | 
			
		||||
                // over 55% of range is completed
 | 
			
		||||
                // under 35% is not done
 | 
			
		||||
                // Blind assumptions if nothing is provided.
 | 
			
		||||
                // over 55% of range is completed.
 | 
			
		||||
                // under 35% is not done.
 | 
			
		||||
                $range = floatval($gi->grademax - $gi->grademin);
 | 
			
		||||
 | 
			
		||||
                if(!$gr->done){
 | 
			
		||||
                    // INCOMPLETe or FAILED
 | 
			
		||||
                if (!$gr->done) {
 | 
			
		||||
                    // INCOMPLETe or FAILED.
 | 
			
		||||
                    $grade =  rand(0, round($range * 0.35) - 1);
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" =>  $gi->grademin+$grade, "fb" =>($grade > 0)?$gr->fb:"" ];
 | 
			
		||||
                }
 | 
			
		||||
                else if(!$gr->result){
 | 
			
		||||
                    //PROGRESS
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => $gi->grademin+rand(round($range * 0.35),round($range * 0.55) - 1 ), "fb" =>$gr->fb ];
 | 
			
		||||
                else if (!$gr->result) {
 | 
			
		||||
                    //PROGRESS.
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => $gi->grademin+rand(round($range * 0.35), round($range * 0.55) - 1 ), "fb" =>$gr->fb ];
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
                    // COMPLETED
 | 
			
		||||
                    // COMPLETED.
 | 
			
		||||
                    $r = (object)["gi" => $g, "grade" => $gi->grademin+rand(round($range * 0.55) , $range  ), "fb" =>$gr->fb ];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,39 +266,38 @@ class gradegenerator {
 | 
			
		|||
        return $rlist;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getstats($student){
 | 
			
		||||
    public function getstats($student) {
 | 
			
		||||
        return $this->table[$student];
 | 
			
		||||
    }
 | 
			
		||||
    public function serialize(): ?string{
 | 
			
		||||
        return json_encode([
 | 
			
		||||
            "table" => $this->table],JSON_PRETTY_PRINT);
 | 
			
		||||
            "table" => $this->table], JSON_PRETTY_PRINT);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function unserialize(string $data): void {
 | 
			
		||||
        $o = json_decode($data,true);
 | 
			
		||||
        $o = json_decode($data, true);
 | 
			
		||||
        $this->table = $o["table"];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function toFile(string $filename){
 | 
			
		||||
    public function toFile(string $filename) {
 | 
			
		||||
        $filename = self::expand_tilde($filename);
 | 
			
		||||
        file_put_contents($filename,$this->serialize());
 | 
			
		||||
        file_put_contents($filename, $this->serialize());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fromFile(string $filename){
 | 
			
		||||
    public function fromFile(string $filename) {
 | 
			
		||||
        $filename = self::expand_tilde($filename);
 | 
			
		||||
        if(file_exists($filename)){
 | 
			
		||||
        if (file_exists($filename)) {
 | 
			
		||||
            try{
 | 
			
		||||
                $json = file_get_contents($filename);
 | 
			
		||||
                $this->unserialize($json);
 | 
			
		||||
            } catch(Exception $x){
 | 
			
		||||
            } catch(Exception $x) {
 | 
			
		||||
                cli_problem("ERROR loading from file");
 | 
			
		||||
                throw $x; // Throw X up again to show the output
 | 
			
		||||
                throw $x; // Throw X up again to show the output.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function expand_tilde($path)
 | 
			
		||||
    {
 | 
			
		||||
    private static function expand_tilde($path) {
 | 
			
		||||
        if (function_exists('posix_getuid') && strpos($path, '~') !== false) {
 | 
			
		||||
            $info = posix_getpwuid(posix_getuid());
 | 
			
		||||
            $path = str_replace('~', $info['dir'], $path);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\helpers;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,40 +31,37 @@ class debugger {
 | 
			
		|||
    private $tag;
 | 
			
		||||
    private $enabled = false;
 | 
			
		||||
 | 
			
		||||
    public function __construct($filename,$tag)
 | 
			
		||||
    {
 | 
			
		||||
    public function __construct($filename, $tag) {
 | 
			
		||||
        global $CFG;
 | 
			
		||||
 | 
			
		||||
        $this->fname = $filename;
 | 
			
		||||
        $this->tag = $tag;
 | 
			
		||||
 | 
			
		||||
        // assume debug environment if cachejs is false
 | 
			
		||||
        // assume debug environment if cachejs is false.
 | 
			
		||||
        $this->enabled = (isset($CFG->cachejs) && $CFG->cachejs == false);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function dump($object,string $tag="")
 | 
			
		||||
    {
 | 
			
		||||
        if(strlen($tag) > 0){
 | 
			
		||||
    public function dump($object, string $tag="") {
 | 
			
		||||
        if (strlen($tag) > 0) {
 | 
			
		||||
            $tag .= ":\n";
 | 
			
		||||
        }
 | 
			
		||||
        $this->writeblock($tag.print_r($object,true));
 | 
			
		||||
        $this->writeblock($tag.print_r($object, true));
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function write($str)
 | 
			
		||||
    {
 | 
			
		||||
    public function write($str) {
 | 
			
		||||
        $this->writeblock($str);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function writeblock($str){
 | 
			
		||||
        if($this->enabled){
 | 
			
		||||
    private function writeblock($str) {
 | 
			
		||||
        if ($this->enabled) {
 | 
			
		||||
            $now = new DateTime();
 | 
			
		||||
 | 
			
		||||
            $tagline = "[ {$this->tag} - ".$now->format("c")." ]";
 | 
			
		||||
 | 
			
		||||
            $fp = fopen($this->fname,"a");
 | 
			
		||||
            fwrite($fp,$tagline . ":\n".$str."\n");
 | 
			
		||||
            $fp = fopen($this->fname, "a");
 | 
			
		||||
            fwrite($fp, $tagline . ":\n".$str."\n");
 | 
			
		||||
            fclose($fp);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\helpers;
 | 
			
		||||
require_once($CFG->dirroot.'/webservice/lib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -17,21 +38,21 @@ class webservicehelper {
 | 
			
		|||
     * @throws \webservice_access_exception If none of the capabilities provided are given to the current user
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public static function has_capabilities($capability,$context){
 | 
			
		||||
    public static function has_capabilities($capability, $context) {
 | 
			
		||||
 | 
			
		||||
        if($context == null){
 | 
			
		||||
        if ($context == null) {
 | 
			
		||||
            $context = \context_system::instance();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(is_array($capability)){
 | 
			
		||||
            //TODO: replace this by accesslib function \has_any_capability()
 | 
			
		||||
            foreach($capability as $cap){
 | 
			
		||||
                if(has_capability($cap,$context)){
 | 
			
		||||
        if (is_array($capability)) {
 | 
			
		||||
            //TODO: replace this by accesslib function \has_any_capability().
 | 
			
		||||
            foreach ($capability as $cap) {
 | 
			
		||||
                if (has_capability($cap, $context)) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        elseif(has_capability($capability,$context)){
 | 
			
		||||
        else if (has_capability($capability, $context)) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -42,32 +63,32 @@ class webservicehelper {
 | 
			
		|||
     * @param \core_course_category  $parent The parent category to use as a scanning base. Used in recursing Leave empty if calling this function
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    public static function has_capability_in_any_category($capability,\core_course_category $parent=null){
 | 
			
		||||
    public static function has_capability_in_any_category($capability, \core_course_category $parent=null) {
 | 
			
		||||
 | 
			
		||||
        // List the categories in which the user has a specific capability
 | 
			
		||||
        // List the categories in which the user has a specific capability.
 | 
			
		||||
        $list = [];
 | 
			
		||||
        // initialize parent if needed
 | 
			
		||||
        if($parent == null){
 | 
			
		||||
        // initialize parent if needed.
 | 
			
		||||
        if ($parent == null) {
 | 
			
		||||
            $parent = \core_course_category::user_top();
 | 
			
		||||
            if(has_capability($capability,$parent->get_context())){
 | 
			
		||||
            if (has_capability($capability, $parent->get_context())) {
 | 
			
		||||
                $list[] = $parent;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $children = $parent->get_children();
 | 
			
		||||
        // Since the change for a category permission is greatest at the lower levels,
 | 
			
		||||
        // we scan in two stages, to focus the search more on the lower levels instead of diving deep into the first category
 | 
			
		||||
        // Stage one (surface check): check all children for the capability
 | 
			
		||||
        foreach($children as $child){
 | 
			
		||||
            // Check if we should add this category
 | 
			
		||||
            if(has_capability($capability,$child->get_context())){
 | 
			
		||||
        // Since the change for a category permission is greatest at the lower levels,.
 | 
			
		||||
        // we scan in two stages, to focus the search more on the lower levels instead of diving deep into the first category.
 | 
			
		||||
        // Stage one (surface check): check all children for the capability.
 | 
			
		||||
        foreach ($children as $child) {
 | 
			
		||||
            // Check if we should add this category.
 | 
			
		||||
            if (has_capability($capability, $child->get_context())) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // Stage two (deep dive): recurse into the child categories
 | 
			
		||||
        foreach($children as $child){
 | 
			
		||||
            if($child->get_children_count() > 0){
 | 
			
		||||
                if(self::has_capability_in_any_category($capability,$child)){
 | 
			
		||||
        // Stage two (deep dive): recurse into the child categories.
 | 
			
		||||
        foreach ($children as $child) {
 | 
			
		||||
            if ($child->get_children_count() > 0) {
 | 
			
		||||
                if (self::has_capability_in_any_category($capability, $child)) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -82,11 +103,11 @@ class webservicehelper {
 | 
			
		|||
     * @param bool $validate Validate the context before checking capabilities
 | 
			
		||||
     * @throws \webservice_access_exception If none of the capabilities provided are given to the current user
 | 
			
		||||
     */
 | 
			
		||||
    public static function require_capabilities($capability,$context=null,$validate=true){
 | 
			
		||||
        if($validate) { 
 | 
			
		||||
    public static function require_capabilities($capability, $context=null, $validate=true) {
 | 
			
		||||
        if ($validate) {
 | 
			
		||||
            \external_api::validate_context($context);
 | 
			
		||||
        }
 | 
			
		||||
        if(! static::has_capabilities($capability,$context)){
 | 
			
		||||
        if (! static::has_capabilities($capability, $context)) {
 | 
			
		||||
            throw new \webservice_access_exception("The capability {$capability} is required on this context ({$context->get_context_name()})");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -98,23 +119,23 @@ class webservicehelper {
 | 
			
		|||
     * @throws \InvalidArgumentException When the context is not found
 | 
			
		||||
     */
 | 
			
		||||
    public static function find_context($contextid): \context{
 | 
			
		||||
        if(isset($contextid) && is_int($contextid) && $contextid > 0){
 | 
			
		||||
        if (isset($contextid) && is_int($contextid) && $contextid > 0) {
 | 
			
		||||
 | 
			
		||||
            if(!in_array($contextid,self::$validated_contexts)){ // Cache the context and make sure it is only validated once...
 | 
			
		||||
            if (!in_array($contextid, self::$validated_contexts)) { // Cache the context and make sure it is only validated once...
 | 
			
		||||
                try{
 | 
			
		||||
                    $context = \context::instance_by_id($contextid);
 | 
			
		||||
                }
 | 
			
		||||
                catch(\dml_missing_record_exception $x){
 | 
			
		||||
                    throw new \InvalidArgumentException("Context {$contextid} not available"); // Just throw it up again. catch is included here to make sure we know it throws this exception
 | 
			
		||||
                catch(\dml_missing_record_exception $x) {
 | 
			
		||||
                    throw new \InvalidArgumentException("Context {$contextid} not available"); // Just throw it up again. catch is included here to make sure we know it throws this exception.
 | 
			
		||||
                }
 | 
			
		||||
                // Validate the found context
 | 
			
		||||
                // Validate the found context.
 | 
			
		||||
                \external_api::validate_context($context);
 | 
			
		||||
                self::$validated_contexts[$contextid] = $context;
 | 
			
		||||
            }
 | 
			
		||||
            return self::$validated_contexts[$contextid];
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            return static::system_context(); // This function ensures the system context is validated just once this call
 | 
			
		||||
            return static::system_context(); // This function ensures the system context is validated just once this call.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +144,7 @@ class webservicehelper {
 | 
			
		|||
     * @return \context_system The system context, validated to use as this context
 | 
			
		||||
     */
 | 
			
		||||
    public static function system_context(): \context_system {
 | 
			
		||||
        if(!isset(static::$systemcontext)){
 | 
			
		||||
        if (!isset(static::$systemcontext)) {
 | 
			
		||||
            static::$systemcontext = \context_system::instance();
 | 
			
		||||
            \external_api::validate_context(static::$systemcontext);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,34 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\ungradedscanners;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class assign_scanner extends scanner_base {
 | 
			
		||||
 | 
			
		||||
    protected function get_ungraded_submissions(){
 | 
			
		||||
    protected function get_ungraded_submissions() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
       //SELECT asgn_sub.id as submissionid, a.id as instanceid, asgn_sub.userid as userid, asgn_sub.timemodified as timesubmitted, asgn_sub.attemptnumber , a.maxattempts
 | 
			
		||||
       //SELECT asgn_sub.id as submissionid, a.id as instanceid, asgn_sub.userid as userid, asgn_sub.timemodified as timesubmitted, asgn_sub.attemptnumber , a.maxattempts.
 | 
			
		||||
       $sql =   "SELECT DISTINCT asgn_sub.userid
 | 
			
		||||
                    FROM {assign_submission} asgn_sub
 | 
			
		||||
                    JOIN {assign} a ON a.id = asgn_sub.assignment
 | 
			
		||||
| 
						 | 
				
			
			@ -22,45 +43,44 @@ class assign_scanner extends scanner_base {
 | 
			
		|||
        return $DB->get_fieldset_sql($sql);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function get_graded_users(){
 | 
			
		||||
    protected function get_graded_users() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $sql = "SELECT DISTINCT g.userid
 | 
			
		||||
                FROM {grade_grades} g
 | 
			
		||||
                LEFT JOIN {grade_items} gi on g.itemid = gi.id
 | 
			
		||||
                WHERE gi.itemmodule = 'assign' AND gi.iteminstance = {$this->gi->iteminstance}
 | 
			
		||||
                AND g.finalgrade IS NOT NULL"; // MAy turn out to be needed, dunno
 | 
			
		||||
                AND g.finalgrade IS NOT NULL"; // MAy turn out to be needed, dunno.
 | 
			
		||||
        return $DB->get_fieldset_sql($sql);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function count_ungraded($course_userids=[]){
 | 
			
		||||
    public function count_ungraded($course_userids=[]) {
 | 
			
		||||
        $ungraded = $this->get_ungraded_submissions();
 | 
			
		||||
 | 
			
		||||
        if(count($course_userids) > 0){
 | 
			
		||||
            $ungraded = array_intersect($ungraded,$course_userids);
 | 
			
		||||
        if (count($course_userids) > 0) {
 | 
			
		||||
            $ungraded = array_intersect($ungraded, $course_userids);
 | 
			
		||||
        }
 | 
			
		||||
        return count($ungraded);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function count_graded($course_userids=[]){
 | 
			
		||||
    public function count_graded($course_userids=[]) {
 | 
			
		||||
        $ungraded = $this->get_ungraded_submissions();
 | 
			
		||||
        $graded = $this->get_graded_users();
 | 
			
		||||
 | 
			
		||||
        if(count($course_userids) > 0){
 | 
			
		||||
            $ungraded = array_intersect($ungraded,$course_userids);
 | 
			
		||||
            $graded = array_intersect($graded,$course_userids);
 | 
			
		||||
        if (count($course_userids) > 0) {
 | 
			
		||||
            $ungraded = array_intersect($ungraded, $course_userids);
 | 
			
		||||
            $graded = array_intersect($graded, $course_userids);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // determine how many id's have a grade, but also an ungraded submission
 | 
			
		||||
        $dual = array_intersect($ungraded,$graded);
 | 
			
		||||
        // subtract those from the graded count
 | 
			
		||||
        // determine how many id's have a grade, but also an ungraded submission.
 | 
			
		||||
        $dual = array_intersect($ungraded, $graded);
 | 
			
		||||
        // subtract those from the graded count.
 | 
			
		||||
        return count($graded) - count($dual);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function has_ungraded_submission($userid)
 | 
			
		||||
    {
 | 
			
		||||
    public function has_ungraded_submission($userid) {
 | 
			
		||||
        $ungraded = $this->get_ungraded_submissions();
 | 
			
		||||
        return in_array($userid,$ungraded);
 | 
			
		||||
        return in_array($userid, $ungraded);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,17 +1,38 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\ungradedscanners;
 | 
			
		||||
 | 
			
		||||
require_once($CFG->dirroot.'/question/engine/states.php'); // for reading question state
 | 
			
		||||
require_once($CFG->dirroot.'/question/engine/states.php'); // for reading question state.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class quiz_scanner extends scanner_base {
 | 
			
		||||
 | 
			
		||||
    protected function get_ungraded_submissions(){
 | 
			
		||||
        // count all users who have one or more questions that still need grading
 | 
			
		||||
    protected function get_ungraded_submissions() {
 | 
			
		||||
        // count all users who have one or more questions that still need grading.
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        // First find all question attempts that need grading
 | 
			
		||||
        // First find all question attempts that need grading.
 | 
			
		||||
        $sql = "SELECT qza.id as submissionid, qza.userid as userid, qas.questionattemptid as attempt_id, qas.sequencenumber as sequencenumber
 | 
			
		||||
                FROM {question_attempt_steps} qas
 | 
			
		||||
                JOIN {question_attempts} qna ON qas.questionattemptid = qna.id
 | 
			
		||||
| 
						 | 
				
			
			@ -20,48 +41,47 @@ class quiz_scanner extends scanner_base {
 | 
			
		|||
 | 
			
		||||
        $rs = $DB->get_recordset_sql($sql);
 | 
			
		||||
        $submissions = [];
 | 
			
		||||
        foreach($rs as $r){
 | 
			
		||||
            // Now, check if 
 | 
			
		||||
        foreach ($rs as $r) {
 | 
			
		||||
            // Now, check if .
 | 
			
		||||
            $maxstate_sql = "SELECT MAX(qas.sequencenumber) FROM {question_attempt_steps} qas WHERE qas.questionattemptid = {$r->attempt_id}";
 | 
			
		||||
            $max = $DB->get_field_sql($maxstate_sql);
 | 
			
		||||
            if($r->sequencenumber == $max){
 | 
			
		||||
                $submissions[$r->userid] = true; // set array index based on user id, to avoid checking if value is in array
 | 
			
		||||
            if ($r->sequencenumber == $max) {
 | 
			
		||||
                $submissions[$r->userid] = true; // set array index based on user id, to avoid checking if value is in array.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $rs->close();
 | 
			
		||||
        return array_keys($submissions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function count_ungraded($course_userids=[]){
 | 
			
		||||
    public function count_ungraded($course_userids=[]) {
 | 
			
		||||
        $ungraded = $this->get_ungraded_submissions();
 | 
			
		||||
        if(count($course_userids) > 0){
 | 
			
		||||
            $ungraded = array_intersect($ungraded,$course_userids);
 | 
			
		||||
        if (count($course_userids) > 0) {
 | 
			
		||||
            $ungraded = array_intersect($ungraded, $course_userids);
 | 
			
		||||
        }
 | 
			
		||||
        return count($ungraded);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function count_graded($course_userids=[]){
 | 
			
		||||
    public function count_graded($course_userids=[]) {
 | 
			
		||||
        // count all users who submitted one or more finished tests.
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $sql = "SELECT DISTINCT g.userid
 | 
			
		||||
                FROM {grade_grades} g
 | 
			
		||||
                LEFT JOIN {grade_items} gi on g.itemid = gi.id
 | 
			
		||||
                WHERE gi.itemmodule = 'quiz' AND gi.iteminstance = {$this->gi->iteminstance}
 | 
			
		||||
                AND g.finalgrade IS NOT NULL"; // MAy turn out to be needed, dunno
 | 
			
		||||
                AND g.finalgrade IS NOT NULL"; // MAy turn out to be needed, dunno.
 | 
			
		||||
 | 
			
		||||
        $graded = $DB->get_fieldset_sql($sql);
 | 
			
		||||
 | 
			
		||||
        if(count($course_userids) > 0){
 | 
			
		||||
            $graded = array_intersect($graded,$course_userids);
 | 
			
		||||
        if (count($course_userids) > 0) {
 | 
			
		||||
            $graded = array_intersect($graded, $course_userids);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return count($graded);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function has_ungraded_submission($userid)
 | 
			
		||||
    {
 | 
			
		||||
    public function has_ungraded_submission($userid) {
 | 
			
		||||
        $ungraded = $this->get_ungraded_submissions();
 | 
			
		||||
        return in_array($userid,$ungraded);
 | 
			
		||||
        return in_array($userid, $ungraded);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\local\ungradedscanners;
 | 
			
		||||
use \grade_item;
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +27,7 @@ use \grade_item;
 | 
			
		|||
abstract class scanner_base {
 | 
			
		||||
    protected $gi;
 | 
			
		||||
 | 
			
		||||
    public function __construct(grade_item $gi){
 | 
			
		||||
    public function __construct(grade_item $gi) {
 | 
			
		||||
        $this->gi = $gi;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -9,17 +30,17 @@ class period {
 | 
			
		|||
    private static $PAGECACHE = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private $r; // Holds database record
 | 
			
		||||
    private $r; // Holds database record.
 | 
			
		||||
    private $id;
 | 
			
		||||
    private $page;
 | 
			
		||||
 | 
			
		||||
    public function aggregator(){ 
 | 
			
		||||
    public function aggregator() {
 | 
			
		||||
        return $this->studyplan->aggregator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cache constructors to avoid multiple creation events in one session.
 | 
			
		||||
    public static function findById($id): self {
 | 
			
		||||
        if(!array_key_exists($id,self::$CACHE)){
 | 
			
		||||
        if (!array_key_exists($id, self::$CACHE)) {
 | 
			
		||||
            self::$CACHE[$id] = new self($id);
 | 
			
		||||
        }
 | 
			
		||||
        return self::$CACHE[$id];
 | 
			
		||||
| 
						 | 
				
			
			@ -28,52 +49,52 @@ class period {
 | 
			
		|||
    /**
 | 
			
		||||
     * Find a period by page and period number [1..$page->periods()]
 | 
			
		||||
     */
 | 
			
		||||
    public static function find(studyplanpage $page,$periodnr): self{
 | 
			
		||||
    public static function find(studyplanpage $page, $periodnr): self{
 | 
			
		||||
        global $DB;
 | 
			
		||||
        if($periodnr < 1){
 | 
			
		||||
            // Clamp period index 
 | 
			
		||||
        if ($periodnr < 1) {
 | 
			
		||||
            // Clamp period index .
 | 
			
		||||
            $periodnr = 1;
 | 
			
		||||
        }
 | 
			
		||||
        try{
 | 
			
		||||
            $id = $DB->get_field(self::TABLE,"id",["page_id"=>$page->id(), "period" => $periodnr],MUST_EXIST);
 | 
			
		||||
            $id = $DB->get_field(self::TABLE, "id", ["page_id"=>$page->id(), "period" => $periodnr], MUST_EXIST);
 | 
			
		||||
            $period = self::findById($id);
 | 
			
		||||
        } catch(\dml_missing_record_exception $x){
 | 
			
		||||
        } catch(\dml_missing_record_exception $x) {
 | 
			
		||||
            // Period does not exist - create one ...
 | 
			
		||||
            // Make a best guess estimate of the start and end date, based on surrounding periods,
 | 
			
		||||
            // or specified duration of the page and the sequence of the periods 
 | 
			
		||||
            // Make a best guess estimate of the start and end date, based on surrounding periods,.
 | 
			
		||||
            // or specified duration of the page and the sequence of the periods .
 | 
			
		||||
            $pcount = $page->periods();
 | 
			
		||||
            $ystart = $page->startdate()->getTimestamp();
 | 
			
		||||
            $yend = $page->enddate()->getTimestamp();
 | 
			
		||||
 | 
			
		||||
            // Estimate the period's timing to make a reasonable first guess
 | 
			
		||||
            // Estimate the period's timing to make a reasonable first guess.
 | 
			
		||||
            $ydelta = $yend - $ystart;
 | 
			
		||||
            $ptime = $ydelta / $pcount;
 | 
			
		||||
 | 
			
		||||
            try{
 | 
			
		||||
                 // Check if we have a previous period to glance the end date of as a reference
 | 
			
		||||
                $startdate = $DB->get_field(self::TABLE,"enddate",["page_id"=>$page->id(), "period" => $periodnr-1],MUST_EXIST);
 | 
			
		||||
                $pstart = strtotime($startdate)+(24*60*60); // Add one day
 | 
			
		||||
            } catch(\dml_missing_record_exception $x2){
 | 
			
		||||
                // If not, do a fair guess
 | 
			
		||||
                 // Check if we have a previous period to glance the end date of as a reference.
 | 
			
		||||
                $startdate = $DB->get_field(self::TABLE, "enddate", ["page_id"=>$page->id(), "period" => $periodnr-1], MUST_EXIST);
 | 
			
		||||
                $pstart = strtotime($startdate)+(24*60*60); // Add one day.
 | 
			
		||||
            } catch(\dml_missing_record_exception $x2) {
 | 
			
		||||
                // If not, do a fair guess.
 | 
			
		||||
                $pstart = $ystart + (($periodnr-1)*$ptime);
 | 
			
		||||
            }
 | 
			
		||||
            try{
 | 
			
		||||
                // Check if we have a next period to glance the start date of as a reference
 | 
			
		||||
               $enddate = $DB->get_field(self::TABLE,"startdate",["page_id"=>$page->id(), "period" => $periodnr+1],MUST_EXIST);
 | 
			
		||||
               $pstart = strtotime($enddate)-(24*60*60); // subtract one day
 | 
			
		||||
           } catch(\dml_missing_record_exception $x2){
 | 
			
		||||
               // If not, do a fair guess
 | 
			
		||||
                // Check if we have a next period to glance the start date of as a reference.
 | 
			
		||||
               $enddate = $DB->get_field(self::TABLE, "startdate", ["page_id"=>$page->id(), "period" => $periodnr+1], MUST_EXIST);
 | 
			
		||||
               $pstart = strtotime($enddate)-(24*60*60); // subtract one day.
 | 
			
		||||
           } catch(\dml_missing_record_exception $x2) {
 | 
			
		||||
               // If not, do a fair guess.
 | 
			
		||||
                $pend = $pstart + $ptime;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // And create the period
 | 
			
		||||
            // And create the period.
 | 
			
		||||
            $period = self::add([
 | 
			
		||||
                'page_id' => $page->id(),
 | 
			
		||||
                'period' => $periodnr,
 | 
			
		||||
                'fullname' => \get_string("period_default_fullname","local_treestudyplan",$periodnr),
 | 
			
		||||
                'shortname' => \get_string("period_default_shortname","local_treestudyplan",$periodnr),
 | 
			
		||||
                'startdate' => date("Y-m-d",$pstart),
 | 
			
		||||
                'enddate' => date("Y-m-d",$pend),
 | 
			
		||||
                'fullname' => \get_string("period_default_fullname", "local_treestudyplan", $periodnr),
 | 
			
		||||
                'shortname' => \get_string("period_default_shortname", "local_treestudyplan", $periodnr),
 | 
			
		||||
                'startdate' => date("Y-m-d", $pstart),
 | 
			
		||||
                'enddate' => date("Y-m-d", $pend),
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
        return $period;
 | 
			
		||||
| 
						 | 
				
			
			@ -81,11 +102,11 @@ class period {
 | 
			
		|||
 | 
			
		||||
    // Cache constructors to avoid multiple creation events in one session.
 | 
			
		||||
    public static function findForPage(studyplanpage $page): array {
 | 
			
		||||
        if(!array_key_exists($page->id(),self::$PAGECACHE)){
 | 
			
		||||
        if (!array_key_exists($page->id(), self::$PAGECACHE)) {
 | 
			
		||||
            $periods = [];
 | 
			
		||||
            // find and add the periods to an array with the period sequence as a key
 | 
			
		||||
            for($i=1; $i <= $page->periods(); $i++){
 | 
			
		||||
                $period = self::find($page,$i); 
 | 
			
		||||
            // find and add the periods to an array with the period sequence as a key.
 | 
			
		||||
            for($i=1; $i <= $page->periods(); $i++) {
 | 
			
		||||
                $period = self::find($page, $i);
 | 
			
		||||
                $periods[$i] = $period;
 | 
			
		||||
            }
 | 
			
		||||
            self::$PAGECACHE[$page->id()] = $periods;
 | 
			
		||||
| 
						 | 
				
			
			@ -96,11 +117,11 @@ class period {
 | 
			
		|||
    private function __construct($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $id]);
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $id]);
 | 
			
		||||
        $this->page = studyplanpage::findById($this->r->page_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -108,37 +129,37 @@ class period {
 | 
			
		|||
        return $this->page->studyplan();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function page(){
 | 
			
		||||
    public function page() {
 | 
			
		||||
        return $this->page;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function shortname(){
 | 
			
		||||
    public function shortname() {
 | 
			
		||||
        return $this->r->shortname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fullname(){
 | 
			
		||||
    public function fullname() {
 | 
			
		||||
        return $this->r->fullname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function period(){
 | 
			
		||||
    public function period() {
 | 
			
		||||
        return $this->r->period;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function startdate(){
 | 
			
		||||
    public function startdate() {
 | 
			
		||||
        return new \DateTime($this->r->startdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function enddate(){
 | 
			
		||||
        if($this->r->enddate && strlen($this->r->enddate) > 0){
 | 
			
		||||
    public function enddate() {
 | 
			
		||||
        if ($this->r->enddate && strlen($this->r->enddate) > 0) {
 | 
			
		||||
            return new \DateTime($this->r->enddate);
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            // return a date 100 years into the future
 | 
			
		||||
            // return a date 100 years into the future.
 | 
			
		||||
            return (new \DateTime($this->r->startdate))->add(new \DateInterval("P100Y"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of period'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'Full name of period'),
 | 
			
		||||
| 
						 | 
				
			
			@ -146,10 +167,10 @@ class period {
 | 
			
		|||
            "period" => new \external_value(PARAM_INT, 'period sequence'),
 | 
			
		||||
            "startdate" => new \external_value(PARAM_TEXT, 'start date of period'),
 | 
			
		||||
            "enddate" => new \external_value(PARAM_TEXT, 'end date of period'),
 | 
			
		||||
        ],'Period info',$value);
 | 
			
		||||
        ], 'Period info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function model(){
 | 
			
		||||
    public function model() {
 | 
			
		||||
        return [
 | 
			
		||||
            'id' => $this->r->id,
 | 
			
		||||
            'fullname' => $this->r->fullname,
 | 
			
		||||
| 
						 | 
				
			
			@ -164,62 +185,62 @@ class period {
 | 
			
		|||
     * Do not use directly to add periods, unless performing import
 | 
			
		||||
     * The static find() and findForPage() functions create the period if needed
 | 
			
		||||
     */
 | 
			
		||||
    public static function add($fields){
 | 
			
		||||
    public static function add($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if(!isset($fields['page_id'])){
 | 
			
		||||
        if (!isset($fields['page_id'])) {
 | 
			
		||||
            throw new \InvalidArgumentException("parameter 'page_id' missing");
 | 
			
		||||
        }
 | 
			
		||||
        if(!isset($fields['period'])){
 | 
			
		||||
        if (!isset($fields['period'])) {
 | 
			
		||||
            throw new \InvalidArgumentException("parameter 'period' missing");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($DB->record_exists(self::TABLE,["page_id"=>$fields["page_id"], "period"=>$fields["period"]])){
 | 
			
		||||
        if ($DB->record_exists(self::TABLE, ["page_id"=>$fields["page_id"], "period"=>$fields["period"]])) {
 | 
			
		||||
            throw new \InvalidArgumentException("record already exists for specified page and period");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $addable = ['page_id','fullname','shortname','period','startdate','enddate'];
 | 
			
		||||
        $addable = ['page_id', 'fullname', 'shortname', 'period', 'startdate', 'enddate'];
 | 
			
		||||
        $info = [ ];
 | 
			
		||||
        foreach($addable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        foreach ($addable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $id = $DB->insert_record(self::TABLE, $info);
 | 
			
		||||
        unset(self::$PAGECACHE[$fields['page_id']]); // invalidate the cache for this page
 | 
			
		||||
        return self::findById($id); // make sure the new page is immediately cached
 | 
			
		||||
        unset(self::$PAGECACHE[$fields['page_id']]); // invalidate the cache for this page.
 | 
			
		||||
        return self::findById($id); // make sure the new page is immediately cached.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function edit($fields){
 | 
			
		||||
    public function edit($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $editable = ['fullname','shortname','startdate','enddate'];
 | 
			
		||||
        $info = ['id' => $this->id,];
 | 
			
		||||
        foreach($editable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        $editable = ['fullname', 'shortname', 'startdate', 'enddate'];
 | 
			
		||||
        $info = ['id' => $this->id, ];
 | 
			
		||||
        foreach ($editable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $DB->update_record(self::TABLE, $info);
 | 
			
		||||
        //reload record after edit
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $this->id],"*",MUST_EXIST);
 | 
			
		||||
        unset(self::$PAGECACHE[$this->r->page_id]); // invalidate the cache for this page
 | 
			
		||||
        //reload record after edit.
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST);
 | 
			
		||||
        unset(self::$PAGECACHE[$this->r->page_id]); // invalidate the cache for this page.
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete(){
 | 
			
		||||
    public function delete() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $DB->delete_records(self::TABLE, ['id' => $this->id]);
 | 
			
		||||
        unset(self::$PAGECACHE[$this->r->page_id]); // invalidate the cache for this page
 | 
			
		||||
        unset(self::$PAGECACHE[$this->r->page_id]); // invalidate the cache for this page.
 | 
			
		||||
        return success::success();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function page_structure($value=VALUE_REQUIRED){
 | 
			
		||||
        return new \external_multiple_structure(self::structure(),"The periods in the page",$value);
 | 
			
		||||
    public static function page_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_multiple_structure(self::structure(), "The periods in the page", $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function page_model(studyplanpage $page){
 | 
			
		||||
    public static function page_model(studyplanpage $page) {
 | 
			
		||||
        $model = [];
 | 
			
		||||
        foreach(self::findForPage($page) as $p){
 | 
			
		||||
        foreach (self::findForPage($page) as $p) {
 | 
			
		||||
            $model[] = $p->model();
 | 
			
		||||
        }
 | 
			
		||||
        return $model;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\privacy;
 | 
			
		||||
use core_privacy\local\metadata\collection;
 | 
			
		||||
| 
						 | 
				
			
			@ -57,9 +78,9 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
     */
 | 
			
		||||
    public static function get_contexts_for_userid(int $userid) : contextlist {
 | 
			
		||||
        $contextlist = new \core_privacy\local\request\contextlist();
 | 
			
		||||
        $contextlist->add_system_context(); // For invitations
 | 
			
		||||
        $contextlist->add_system_context(); // For invitations.
 | 
			
		||||
 | 
			
		||||
        // add contexts for linked studyplans
 | 
			
		||||
        // add contexts for linked studyplans.
 | 
			
		||||
        $sql = "SELECT s.context_id FROM {local_treestudyplan} s
 | 
			
		||||
                    INNER JOIN {local_treestudyplan_user} a ON a.studyplan_id = s.id
 | 
			
		||||
                WHERE ( a.user_id = :userid )
 | 
			
		||||
| 
						 | 
				
			
			@ -74,39 +95,39 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
     *
 | 
			
		||||
     * @param   approved_contextlist    $contextlist    The approved contexts to export information for.
 | 
			
		||||
     */
 | 
			
		||||
    public static function export_user_data(approved_contextlist $contextlist){
 | 
			
		||||
    public static function export_user_data(approved_contextlist $contextlist) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        foreach ($contextlist->get_contexts() as $context) {
 | 
			
		||||
            $user = $contextlist->get_user();
 | 
			
		||||
 | 
			
		||||
            if($context instanceof \context_system){
 | 
			
		||||
                // Export invitations
 | 
			
		||||
            if ($context instanceof \context_system) {
 | 
			
		||||
                // Export invitations.
 | 
			
		||||
                $sql = "SELECT * FROM {local_treestudyplan_invit} i
 | 
			
		||||
                        WHERE ( aiuser_id = :userid )
 | 
			
		||||
                ";
 | 
			
		||||
                $records = $DB->get_records_sql($sql,["userid" => $user->id]);
 | 
			
		||||
                foreach($records as $r) {
 | 
			
		||||
                $records = $DB->get_records_sql($sql, ["userid" => $user->id]);
 | 
			
		||||
                foreach ($records as $r) {
 | 
			
		||||
                    static::export_invitation_data_for_user($r);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Export empty associations
 | 
			
		||||
                // Export empty associations.
 | 
			
		||||
                $sql = "SELECT * FROM {local_treestudyplan} s
 | 
			
		||||
                        INNER JOIN {local_treestudyplan_user} a ON a.studyplan_id = s.id
 | 
			
		||||
                    WHERE ( a.user_id = :userid AND (s.context_id IS NULL or s.context_id = 0)
 | 
			
		||||
                ";
 | 
			
		||||
                $records = $DB->get_records_sql($sql,["userid" => $user->id, "contextid" => $context->id]);
 | 
			
		||||
                foreach($records as $r) {
 | 
			
		||||
                $records = $DB->get_records_sql($sql, ["userid" => $user->id, "contextid" => $context->id]);
 | 
			
		||||
                foreach ($records as $r) {
 | 
			
		||||
                    static::export_studyplan_data_for_user($r);
 | 
			
		||||
                }
 | 
			
		||||
            } else if ($context->contextlevel == CONTEXT_COURSECAT){
 | 
			
		||||
               // Export studyplan associations
 | 
			
		||||
            } else if ($context->contextlevel == CONTEXT_COURSECAT) {
 | 
			
		||||
               // Export studyplan associations.
 | 
			
		||||
                $sql = "SELECT * FROM {local_treestudyplan} s
 | 
			
		||||
                        INNER JOIN {local_treestudyplan_user} a ON a.studyplan_id = s.id
 | 
			
		||||
                    WHERE ( a.user_id = :userid AND s.context_id = :contextid)
 | 
			
		||||
                ";
 | 
			
		||||
                $records = $DB->get_records_sql($sql,["userid" => $user->id, "contextid" => $context->id]);
 | 
			
		||||
                foreach($records as $r) {
 | 
			
		||||
                $records = $DB->get_records_sql($sql, ["userid" => $user->id, "contextid" => $context->id]);
 | 
			
		||||
                foreach ($records as $r) {
 | 
			
		||||
                    static::export_studyplan_data_for_user($r);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -149,15 +170,15 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
     *
 | 
			
		||||
     * @param   context                 $context   The specific context to delete data for.
 | 
			
		||||
     */
 | 
			
		||||
    public static function delete_data_for_all_users_in_context(\context $context){
 | 
			
		||||
    public static function delete_data_for_all_users_in_context(\context $context) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        // find studyplans in context
 | 
			
		||||
        if($context->contextlevel == CONTEXT_COURSECAT){
 | 
			
		||||
        // find studyplans in context.
 | 
			
		||||
        if ($context->contextlevel == CONTEXT_COURSECAT) {
 | 
			
		||||
            $sql = "SELECT s.id FROM {local_treestudyplan} WHERE ( a.user_id = :userid AND s.context_id = :contextid)";
 | 
			
		||||
            $plan_ids = $DB->get_fieldset_sql($sql,["contextid"=>$context->id]);
 | 
			
		||||
            $plan_ids = $DB->get_fieldset_sql($sql, ["contextid"=>$context->id]);
 | 
			
		||||
 | 
			
		||||
            foreach($plan_ids as $plan_id){
 | 
			
		||||
                $DB->delete_records("local_treestudyplan_user",["studyplan_id" => $plan_id]);
 | 
			
		||||
            foreach ($plan_ids as $plan_id) {
 | 
			
		||||
                $DB->delete_records("local_treestudyplan_user", ["studyplan_id" => $plan_id]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -167,32 +188,32 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
     *
 | 
			
		||||
     * @param   approved_contextlist    $contextlist    The approved contexts and user information to delete information for.
 | 
			
		||||
     */
 | 
			
		||||
    public static function delete_data_for_user(approved_contextlist $contextlist){
 | 
			
		||||
    public static function delete_data_for_user(approved_contextlist $contextlist) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $user = $contextlist->get_user();
 | 
			
		||||
 | 
			
		||||
        foreach ($contextlist->get_contexts() as $context) {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if($context->contextlevel == CONTEXT_SYSTEM){
 | 
			
		||||
            if ($context->contextlevel == CONTEXT_SYSTEM) {
 | 
			
		||||
                $sql = "SELECT s.id FROM {local_treestudyplan} INNER JOIN {local_treestudyplan_user} a ON a.studyplan_id = s.id
 | 
			
		||||
                        WHERE ( a.user_id = :userid AND ( s.context_id IS NULL OR s.context_id == 0 OR s.context_id = :contextid))";
 | 
			
		||||
                $plan_ids = $DB->get_fieldset_sql($sql,["contextid"=>$context->id, "userid" => $user->id]);
 | 
			
		||||
                $plan_ids = $DB->get_fieldset_sql($sql, ["contextid"=>$context->id, "userid" => $user->id]);
 | 
			
		||||
 | 
			
		||||
                foreach($plan_ids as $plan_id){
 | 
			
		||||
                    $DB->delete_records("local_treestudyplan_user",["studyplan_id" => $plan_id,"user_id" => $user->id]);
 | 
			
		||||
                foreach ($plan_ids as $plan_id) {
 | 
			
		||||
                    $DB->delete_records("local_treestudyplan_user", ["studyplan_id" => $plan_id, "user_id" => $user->id]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Also delete all invitations for this user
 | 
			
		||||
                $DB->delete_records("local_treestudyplan_invit",["user_id" => $user->id]);
 | 
			
		||||
                // Also delete all invitations for this user.
 | 
			
		||||
                $DB->delete_records("local_treestudyplan_invit", ["user_id" => $user->id]);
 | 
			
		||||
 | 
			
		||||
            } else if ($context->contextlevel == CONTEXT_COURSECAT){
 | 
			
		||||
            } else if ($context->contextlevel == CONTEXT_COURSECAT) {
 | 
			
		||||
                $sql = "SELECT s.id FROM {local_treestudyplan} INNER JOIN {local_treestudyplan_user} a ON a.studyplan_id = s.id
 | 
			
		||||
                        WHERE ( a.user_id = :userid AND s.context_id = :contextid)";
 | 
			
		||||
                $plan_ids = $DB->get_fieldset_sql($sql,["contextid"=>$context->id, "userid" => $user->id]);
 | 
			
		||||
                $plan_ids = $DB->get_fieldset_sql($sql, ["contextid"=>$context->id, "userid" => $user->id]);
 | 
			
		||||
 | 
			
		||||
                foreach($plan_ids as $plan_id){
 | 
			
		||||
                    $DB->delete_records("local_treestudyplan_user",["studyplan_id" => $plan_id,"user_id" => $user->id]);
 | 
			
		||||
                foreach ($plan_ids as $plan_id) {
 | 
			
		||||
                    $DB->delete_records("local_treestudyplan_user", ["studyplan_id" => $plan_id, "user_id" => $user->id]);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -203,16 +224,16 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
     *
 | 
			
		||||
     * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
 | 
			
		||||
     */
 | 
			
		||||
    public static function get_users_in_context(userlist $userlist){
 | 
			
		||||
    public static function get_users_in_context(userlist $userlist) {
 | 
			
		||||
        $context = $userlist->get_context();
 | 
			
		||||
 | 
			
		||||
        if ($context instanceof \context_system) {
 | 
			
		||||
            // Add all invitations
 | 
			
		||||
            // Add all invitations.
 | 
			
		||||
            $sql = "SELECT i.user_id as userid
 | 
			
		||||
            FROM {local_treestudyplan_invit} i;";
 | 
			
		||||
            $userlist->add_from_sql('userid', $sql, []);
 | 
			
		||||
 | 
			
		||||
            // also add "contextless studyplans, they are considered in system context"
 | 
			
		||||
            // also add "contextless studyplans, they are considered in system context".
 | 
			
		||||
            $sql = "SELECT a.user_id as userid FROM {local_treestudyplan_user} a
 | 
			
		||||
                    INNER JOIN {local_treestudyplan} s ON a.studyplan_id = s.id
 | 
			
		||||
                WHERE ( a.context_id is NULL or a.context_id = 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -221,7 +242,7 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // add the links to all study plans in this context
 | 
			
		||||
        // add the links to all study plans in this context.
 | 
			
		||||
        $sql = "SELECT a.user_id as userid FROM {local_treestudyplan_user} a
 | 
			
		||||
                    INNER JOIN {local_treestudyplan} s ON a.studyplan_id = s.id
 | 
			
		||||
                WHERE ( a.context_id = :contextid )
 | 
			
		||||
| 
						 | 
				
			
			@ -235,36 +256,36 @@ class provider implements   \core_privacy\local\metadata\provider,
 | 
			
		|||
     *
 | 
			
		||||
     * @param   approved_userlist       $userlist The approved context and user information to delete information for.
 | 
			
		||||
     */
 | 
			
		||||
    public static function delete_data_for_users(approved_userlist $userlist){
 | 
			
		||||
    public static function delete_data_for_users(approved_userlist $userlist) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $context = $userlist->get_context();
 | 
			
		||||
        $users = $userlist->get_userids();
 | 
			
		||||
        list($userinsql, $userinparams) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED,'user');
 | 
			
		||||
        list($userinsql, $userinparams) = $DB->get_in_or_equal($users, SQL_PARAMS_NAMED, 'user');
 | 
			
		||||
 | 
			
		||||
        $plan_ids = [];
 | 
			
		||||
        if($context->contextlevel == CONTEXT_SYSTEM){
 | 
			
		||||
            // Determine the relevant plan_ids for this context
 | 
			
		||||
        if ($context->contextlevel == CONTEXT_SYSTEM) {
 | 
			
		||||
            // Determine the relevant plan_ids for this context.
 | 
			
		||||
            $sql = "SELECT s.id FROM {local_treestudyplan}
 | 
			
		||||
                    WHERE ( s.context_id IS NULL OR s.context_id == 0 OR s.context_id = :contextid)) ";
 | 
			
		||||
            $plan_ids = $DB->get_fieldset_sql($sql,["contextid"=>$context->id,]);
 | 
			
		||||
            // If plan ids not empty, they will be processed later
 | 
			
		||||
            $plan_ids = $DB->get_fieldset_sql($sql, ["contextid"=>$context->id, ]);
 | 
			
		||||
            // If plan ids not empty, they will be processed later.
 | 
			
		||||
 | 
			
		||||
            // Also delete all invitations for these users
 | 
			
		||||
            // Also delete all invitations for these users.
 | 
			
		||||
            $sql = "user_id {$userinsql}";
 | 
			
		||||
            $DB->delete_records_select("local_treestudyplan_invit",$sql,$userinparams);
 | 
			
		||||
            $DB->delete_records_select("local_treestudyplan_invit", $sql, $userinparams);
 | 
			
		||||
 | 
			
		||||
        } else if ($context->contextlevel == CONTEXT_COURSECAT){
 | 
			
		||||
        } else if ($context->contextlevel == CONTEXT_COURSECAT) {
 | 
			
		||||
            $sql = "SELECT s.id FROM {local_treestudyplan}
 | 
			
		||||
                    WHERE (s.context_id = :contextid)";
 | 
			
		||||
            $plan_ids = $DB->get_fieldset_sql($sql,["contextid"=>$context->id,]);
 | 
			
		||||
            // If plan ids not empty, they will be processed later
 | 
			
		||||
            $plan_ids = $DB->get_fieldset_sql($sql, ["contextid"=>$context->id, ]);
 | 
			
		||||
            // If plan ids not empty, they will be processed later.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Now delete the studyplan associations if relevant
 | 
			
		||||
        if(count($plan_ids) >0 && count($users) >0){
 | 
			
		||||
        // Now delete the studyplan associations if relevant.
 | 
			
		||||
        if (count($plan_ids) >0 && count($users) >0) {
 | 
			
		||||
 | 
			
		||||
            list($planinsql,$planinputparams) = $DB->get_in_or_equal($plan_ids, SQL_PARAMS_NAMED,'plan');
 | 
			
		||||
            list($planinsql, $planinputparams) = $DB->get_in_or_equal($plan_ids, SQL_PARAMS_NAMED, 'plan');
 | 
			
		||||
            $params = $userinparams+$planinputparams;
 | 
			
		||||
            $sql = "user_id {$userinsql} and studyplan_id {$planinsql}";
 | 
			
		||||
            $DB->delete_records_select('local_treestudyplan_user', $sql, $params);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +1,37 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("$CFG->libdir/formslib.php");
 | 
			
		||||
require_once("$CFG->dirroot/local/treestudyplan/lib.php");
 | 
			
		||||
 | 
			
		||||
class reportinvite_form extends moodleform {
 | 
			
		||||
    //Add elements to form
 | 
			
		||||
	const GOALS_EDITOR_OPTIONS = array('trusttext'=>true, 'subdirs'=>true, 'maxfiles'=>0,'maxbytes'=>5*1024*1025);
 | 
			
		||||
    //Add elements to form.
 | 
			
		||||
	const GOALS_EDITOR_OPTIONS = array('trusttext'=>true, 'subdirs'=>true, 'maxfiles'=>0, 'maxbytes'=>5*1024*1025);
 | 
			
		||||
 | 
			
		||||
    public function definition() {
 | 
			
		||||
        global $CFG;
 | 
			
		||||
 | 
			
		||||
	    // 'code', 'revision', 'description', 'goals', 'complexity', 'points', 'studyhours'
 | 
			
		||||
        $mform = $this->_form; // Don't forget the underscore! 
 | 
			
		||||
	    // 'code', 'revision', 'description', 'goals', 'complexity', 'points', 'studyhours'.
 | 
			
		||||
        $mform = $this->_form; // Don't forget the underscore! .
 | 
			
		||||
		
 | 
			
		||||
        $mform->addElement('hidden', 'add', 0);
 | 
			
		||||
        $mform->setType('add', PARAM_ALPHANUM);
 | 
			
		||||
| 
						 | 
				
			
			@ -18,25 +39,25 @@ class reportinvite_form extends moodleform {
 | 
			
		|||
        $mform->addElement('hidden', 'update', 0);
 | 
			
		||||
        $mform->setType('update', PARAM_INT);
 | 
			
		||||
 | 
			
		||||
//		$mform->addElement('static', 'desc_new', get_string('invite_desc_new','local_treestudyplan')); // Add elements to your form
 | 
			
		||||
//		$mform->addElement('static', 'desc_edit', get_string('invite_desc_edit','local_treestudyplan')); // Add elements to your form
 | 
			
		||||
//		$mform->addElement('static', 'desc_new', get_string('invite_desc_new', 'local_treestudyplan')); // Add elements to your form.
 | 
			
		||||
//		$mform->addElement('static', 'desc_edit', get_string('invite_desc_edit', 'local_treestudyplan')); // Add elements to your form.
 | 
			
		||||
 | 
			
		||||
        $mform->addElement('text', 'name', get_string('invite_name','local_treestudyplan'), array('size' => 50)); // Add elements to your form
 | 
			
		||||
        $mform->setType('name', PARAM_NOTAGS);                   //Set type of element
 | 
			
		||||
        $mform->setDefault('name', '');        //Default value
 | 
			
		||||
        $mform->addElement('text', 'name', get_string('invite_name', 'local_treestudyplan'), array('size' => 50)); // Add elements to your form.
 | 
			
		||||
        $mform->setType('name', PARAM_NOTAGS);                   //Set type of element.
 | 
			
		||||
        $mform->setDefault('name', '');        //Default value.
 | 
			
		||||
		$mform->addRule('name', get_string('required'), 'required', null, 'client');
 | 
			
		||||
 | 
			
		||||
        $mform->addElement('text', 'email', get_string('invite_email','local_treestudyplan'), array('size' => 20)); // Add elements to your form
 | 
			
		||||
        $mform->setType('email', PARAM_NOTAGS);                   //Set type of element
 | 
			
		||||
        $mform->setDefault('email', '');        //Default value
 | 
			
		||||
        $mform->addElement('text', 'email', get_string('invite_email', 'local_treestudyplan'), array('size' => 20)); // Add elements to your form.
 | 
			
		||||
        $mform->setType('email', PARAM_NOTAGS);                   //Set type of element.
 | 
			
		||||
        $mform->setDefault('email', '');        //Default value.
 | 
			
		||||
		$mform->addRule('email', get_string('required'), 'required', null, 'client');
 | 
			
		||||
		$mform->addRule('email', get_string('email'), 'email', null, 'client');
 | 
			
		||||
 | 
			
		||||
        $mform->addElement('static', get_string('invite_email','local_treestudyplan') ); // Add elements to your form
 | 
			
		||||
        $mform->addElement('static', get_string('invite_email', 'local_treestudyplan') ); // Add elements to your form.
 | 
			
		||||
 | 
			
		||||
		$this->add_action_buttons();
 | 
			
		||||
    }
 | 
			
		||||
    //Custom validation should be added here
 | 
			
		||||
    //Custom validation should be added here.
 | 
			
		||||
    function validation($data, $files) {
 | 
			
		||||
        return array();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -48,17 +69,17 @@ class reportinvite_form extends moodleform {
 | 
			
		|||
 | 
			
		||||
	function get_data()
 | 
			
		||||
	{
 | 
			
		||||
		global $DB,$USER;
 | 
			
		||||
		global $DB, $USER;
 | 
			
		||||
 | 
			
		||||
		$data = parent::get_data();
 | 
			
		||||
		if($data != NULL)
 | 
			
		||||
		if ($data != NULL)
 | 
			
		||||
		{
 | 
			
		||||
			if(empty($data->user_id))
 | 
			
		||||
			if (empty($data->user_id))
 | 
			
		||||
			{
 | 
			
		||||
				$data->user_id = $USER->id;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(empty($data->update))
 | 
			
		||||
			if (empty($data->update))
 | 
			
		||||
			{
 | 
			
		||||
				$date = new DateTime("now", core_date::get_user_timezone_object());
 | 
			
		||||
				$date->setTime(0, 0, 0);	
 | 
			
		||||
| 
						 | 
				
			
			@ -66,9 +87,9 @@ class reportinvite_form extends moodleform {
 | 
			
		|||
				$data->idate = $date->getTimeStamp();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if(empty($data->update))
 | 
			
		||||
			if (empty($data->update))
 | 
			
		||||
			{
 | 
			
		||||
				//create a new random key for the invite
 | 
			
		||||
				//create a new random key for the invite.
 | 
			
		||||
				do {
 | 
			
		||||
					$length = 20;
 | 
			
		||||
					$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +99,7 @@ class reportinvite_form extends moodleform {
 | 
			
		|||
						$randomkey .= $characters[rand(0, $charactersLength - 1)];
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// Double check that the key is unique before inserting
 | 
			
		||||
					// Double check that the key is unique before inserting.
 | 
			
		||||
				} while($DB->record_exists_select("local_treestudyplan_invit", $DB->sql_compare_text("invitekey"). " = " . $DB->sql_compare_text(":invitekey"), ['invitekey' => $randomkey]));
 | 
			
		||||
 | 
			
		||||
				$data->invitekey = $randomkey;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -29,15 +50,14 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function list_user_studyplans($userid){
 | 
			
		||||
    private static function list_user_studyplans($userid) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $studyplans = studyplan::find_for_user($userid);
 | 
			
		||||
        foreach($studyplans as $studyplan)
 | 
			
		||||
        {
 | 
			
		||||
            // only include studyplans in the context the user has permissions for
 | 
			
		||||
            if(webservicehelper::has_capabilities(self::CAP_VIEWOTHER,$studyplan->context(),false)){
 | 
			
		||||
        foreach ($studyplans as $studyplan) {
 | 
			
		||||
            // only include studyplans in the context the user has permissions for.
 | 
			
		||||
            if (webservicehelper::has_capabilities(self::CAP_VIEWOTHER, $studyplan->context(), false)) {
 | 
			
		||||
                $list[] =$studyplan->simple_model();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -63,17 +83,15 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
            studyplan::user_structure()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    public static function get_user_studyplans($userid)
 | 
			
		||||
    {
 | 
			
		||||
    public static function get_user_studyplans($userid) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $studyplans = studyplan::find_for_user($userid);
 | 
			
		||||
 | 
			
		||||
        $map = [];
 | 
			
		||||
        foreach($studyplans as $studyplan)
 | 
			
		||||
        {
 | 
			
		||||
            // only include studyplans in the context the user has permissions for
 | 
			
		||||
            if(webservicehelper::has_capabilities(self::CAP_VIEWOTHER,$studyplan->context(),false)){
 | 
			
		||||
        foreach ($studyplans as $studyplan) {
 | 
			
		||||
            // only include studyplans in the context the user has permissions for.
 | 
			
		||||
            if (webservicehelper::has_capabilities(self::CAP_VIEWOTHER, $studyplan->context(), false)) {
 | 
			
		||||
                $map[] = $studyplan->user_model($userid);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -99,14 +117,13 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
	{
 | 
			
		||||
        return studyplan::user_structure();
 | 
			
		||||
    }
 | 
			
		||||
    public static function get_user_studyplan($userid,$studyplanid)
 | 
			
		||||
    {
 | 
			
		||||
    public static function get_user_studyplan($userid, $studyplanid) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $studyplan = studyplan::findById($studyplanid);
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEWOTHER,$studyplan->context());
 | 
			
		||||
        webservicehelper::require_capabilities(self::CAP_VIEWOTHER, $studyplan->context());
 | 
			
		||||
 | 
			
		||||
        if($studyplan->has_linked_user($userid)){
 | 
			
		||||
        if ($studyplan->has_linked_user($userid)) {
 | 
			
		||||
            return $studyplan->user_model($userid);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -133,25 +150,23 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
            studyplan::user_structure()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    public static function get_invited_studyplan($invitekey)
 | 
			
		||||
    {
 | 
			
		||||
    public static function get_invited_studyplan($invitekey) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $invite = $DB->get_record_select("local_treestudyplan_invit", $DB->sql_compare_text("invitekey"). " = " . $DB->sql_compare_text(":invitekey"), ['invitekey' => $invitekey]);
 | 
			
		||||
 | 
			
		||||
        if(empty($invite)){
 | 
			
		||||
        if (empty($invite)) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Validate context now
 | 
			
		||||
        // Validate context now.
 | 
			
		||||
        \external_api::validate_context(\context_system::instance());
 | 
			
		||||
 | 
			
		||||
        $userid = $invite->user_id;
 | 
			
		||||
 | 
			
		||||
        $map = [];
 | 
			
		||||
        $studyplans = studyplan::find_for_user($userid);
 | 
			
		||||
        foreach($studyplans as $studyplan)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($studyplans as $studyplan) {
 | 
			
		||||
            $map[] = $studyplan->user_model($userid);
 | 
			
		||||
        }
 | 
			
		||||
        return $map;
 | 
			
		||||
| 
						 | 
				
			
			@ -175,14 +190,13 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function list_own_studyplans(){
 | 
			
		||||
    private static function list_own_studyplans() {
 | 
			
		||||
        global $CFG, $DB, $USER;
 | 
			
		||||
        $userid = $USER->id;
 | 
			
		||||
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $studyplans = studyplan::find_for_user($userid);
 | 
			
		||||
        foreach($studyplans as $studyplan)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($studyplans as $studyplan) {
 | 
			
		||||
            $list[] =$studyplan->simple_model();
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
| 
						 | 
				
			
			@ -208,8 +222,7 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function get_own_studyplan($id=null)
 | 
			
		||||
    {
 | 
			
		||||
    public static function get_own_studyplan($id=null) {
 | 
			
		||||
        global $USER;
 | 
			
		||||
 | 
			
		||||
        // Validate this call in the system context.
 | 
			
		||||
| 
						 | 
				
			
			@ -219,8 +232,8 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
 | 
			
		||||
        $studyplans = studyplan::find_for_user($userid);
 | 
			
		||||
 | 
			
		||||
        if(isset($id) && $id > 0){
 | 
			
		||||
            if(isset($studyplans[$id])){
 | 
			
		||||
        if (isset($id) && $id > 0) {
 | 
			
		||||
            if (isset($studyplans[$id])) {
 | 
			
		||||
                $studyplan = $studyplans[$id];
 | 
			
		||||
                return [$studyplan->user_model($userid)];
 | 
			
		||||
            } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -229,7 +242,7 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            $map = [];
 | 
			
		||||
            foreach($studyplans as $studyplan){
 | 
			
		||||
            foreach ($studyplans as $studyplan) {
 | 
			
		||||
                $map[] = $studyplan->user_model($userid);
 | 
			
		||||
            }
 | 
			
		||||
            return $map;
 | 
			
		||||
| 
						 | 
				
			
			@ -256,16 +269,15 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function get_teaching_studyplans($id=null)
 | 
			
		||||
    {
 | 
			
		||||
    public static function get_teaching_studyplans($id=null) {
 | 
			
		||||
        global $CFG, $DB, $USER;
 | 
			
		||||
        $userid = $USER->id;
 | 
			
		||||
 | 
			
		||||
        \external_api::validate_context(\context_system::instance());
 | 
			
		||||
        $studyplans = teachingfinder::list_my_plans();
 | 
			
		||||
 | 
			
		||||
        if(isset($id) && $id > 0){
 | 
			
		||||
            if(isset($studyplans[$id])){
 | 
			
		||||
        if (isset($id) && $id > 0) {
 | 
			
		||||
            if (isset($studyplans[$id])) {
 | 
			
		||||
                $studyplan = $studyplans[$id];
 | 
			
		||||
                return [$studyplan->editor_model($userid)];
 | 
			
		||||
            } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -274,7 +286,7 @@ class studentstudyplanservice extends \external_api
 | 
			
		|||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            $map = [];
 | 
			
		||||
            foreach($studyplans as $studyplan){
 | 
			
		||||
            foreach ($studyplans as $studyplan) {
 | 
			
		||||
                $map[] = $studyplan->editor_model($userid);
 | 
			
		||||
            }
 | 
			
		||||
            return $map;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +37,7 @@ class studyitem {
 | 
			
		|||
    public const TABLE = "local_treestudyplan_item";
 | 
			
		||||
 | 
			
		||||
    private static $STUDYITEM_CACHE = [];
 | 
			
		||||
    private $r; // Holds database record
 | 
			
		||||
    private $r; // Holds database record.
 | 
			
		||||
    private $id;
 | 
			
		||||
 | 
			
		||||
    private $courseinfo = null;
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +57,7 @@ class studyitem {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    public static function findById($id): self {
 | 
			
		||||
        if(!array_key_exists($id,self::$STUDYITEM_CACHE)){
 | 
			
		||||
        if (!array_key_exists($id, self::$STUDYITEM_CACHE)) {
 | 
			
		||||
            self::$STUDYITEM_CACHE[$id] = new self($id);
 | 
			
		||||
        }
 | 
			
		||||
        return self::$STUDYITEM_CACHE[$id];
 | 
			
		||||
| 
						 | 
				
			
			@ -46,38 +67,38 @@ class studyitem {
 | 
			
		|||
    public function __construct($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $id],"*",MUST_EXIST);
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $id], "*", MUST_EXIST);
 | 
			
		||||
 | 
			
		||||
        $this->studyline = studyline::findById($this->r->line_id);
 | 
			
		||||
        $this->aggregator = $this->studyline()->studyplan()->aggregator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function slot(){
 | 
			
		||||
    public function slot() {
 | 
			
		||||
        return $this->r->slot;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function layer(){
 | 
			
		||||
    public function layer() {
 | 
			
		||||
        return $this->r->layer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function type(){
 | 
			
		||||
    public function type() {
 | 
			
		||||
        return $this->r->type;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function courseid(){
 | 
			
		||||
    public function courseid() {
 | 
			
		||||
        return $this->r->course_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function exists($id){
 | 
			
		||||
    public static function exists($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        return is_numeric($id) && $DB->record_exists(self::TABLE, array('id' => $id));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of study item'),
 | 
			
		||||
            "type" => new \external_value(PARAM_TEXT, 'shortname of study item'),
 | 
			
		||||
| 
						 | 
				
			
			@ -96,17 +117,17 @@ class studyitem {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(){
 | 
			
		||||
    public function editor_model() {
 | 
			
		||||
        return $this->generate_model("editor");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function generate_model($mode){
 | 
			
		||||
        // Mode parameter is used to geep this function for both editor model and export model
 | 
			
		||||
        // (Export model results in fewer parameters on children, but is otherwise basically the same as this function)
 | 
			
		||||
    private function generate_model($mode) {
 | 
			
		||||
        // Mode parameter is used to geep this function for both editor model and export model.
 | 
			
		||||
        // (Export model results in fewer parameters on children, but is otherwise basically the same as this function).
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
            'id' => $this->r->id, // Id is needed in export model because of link references
 | 
			
		||||
            'id' => $this->r->id, // Id is needed in export model because of link references.
 | 
			
		||||
            'type' => $this->isValid()?$this->r->type:self::INVALID,
 | 
			
		||||
            'conditions' => $this->r->conditions,
 | 
			
		||||
            'slot' => $this->r->slot,
 | 
			
		||||
| 
						 | 
				
			
			@ -118,68 +139,67 @@ class studyitem {
 | 
			
		|||
                "out" => [],
 | 
			
		||||
            ]
 | 
			
		||||
        ];
 | 
			
		||||
        if($mode == "export"){
 | 
			
		||||
            // remove slot and layer
 | 
			
		||||
        if ($mode == "export") {
 | 
			
		||||
            // remove slot and layer.
 | 
			
		||||
            unset($model["slot"]);
 | 
			
		||||
            unset($model["layer"]);
 | 
			
		||||
            unset($model["continuation_id"]);
 | 
			
		||||
            $model["connections"] = []; // In export mode, connections is just an array of outgoing connections
 | 
			
		||||
            if(!isset($this->r->conditions)){
 | 
			
		||||
            $model["connections"] = []; // In export mode, connections is just an array of outgoing connections.
 | 
			
		||||
            if (!isset($this->r->conditions)) {
 | 
			
		||||
                unset($model["conditions"]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add course link if available
 | 
			
		||||
        // Add course link if available.
 | 
			
		||||
        $ci = $this->getcourseinfo();
 | 
			
		||||
        if(isset($ci)){
 | 
			
		||||
            if($mode == "export"){
 | 
			
		||||
        if (isset($ci)) {
 | 
			
		||||
            if ($mode == "export") {
 | 
			
		||||
                $model['course'] = $ci->shortname();
 | 
			
		||||
            } else {
 | 
			
		||||
                $model['course'] = $ci->editor_model($this,$this->aggregator->usecorecompletioninfo());
 | 
			
		||||
                $model['course'] = $ci->editor_model($this, $this->aggregator->usecorecompletioninfo());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add badge info if available
 | 
			
		||||
        if(is_numeric($this->r->badge_id) && $DB->record_exists('badge', array('id' => $this->r->badge_id)))
 | 
			
		||||
        {
 | 
			
		||||
        // Add badge info if available.
 | 
			
		||||
        if (is_numeric($this->r->badge_id) && $DB->record_exists('badge', array('id' => $this->r->badge_id))) {
 | 
			
		||||
            $badge = new \core_badges\badge($this->r->badge_id);
 | 
			
		||||
            $badgeinfo = new badgeinfo($badge);
 | 
			
		||||
            if($mode == "export"){
 | 
			
		||||
            if ($mode == "export") {
 | 
			
		||||
                $model['badge'] = $badgeinfo->name();
 | 
			
		||||
            } else {
 | 
			
		||||
                // Also supply a list of linked users, so the badgeinfo can give stats on 
 | 
			
		||||
                // the amount issued, related to this studyplan
 | 
			
		||||
                // Also supply a list of linked users, so the badgeinfo can give stats on .
 | 
			
		||||
                // the amount issued, related to this studyplan.
 | 
			
		||||
                $studentids = $this->studyline()->studyplan()->find_linked_userids();
 | 
			
		||||
                $model['badge'] = $badgeinfo->editor_model($studentids);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($mode == "export"){
 | 
			
		||||
            // Also export gradables
 | 
			
		||||
        if ($mode == "export") {
 | 
			
		||||
            // Also export gradables.
 | 
			
		||||
            $gradables = gradeinfo::list_studyitem_gradables($this);
 | 
			
		||||
            if(count($gradables) > 0){
 | 
			
		||||
            if (count($gradables) > 0) {
 | 
			
		||||
                $model["gradables"] = [];
 | 
			
		||||
                foreach($gradables as $g){
 | 
			
		||||
                foreach ($gradables as $g) {
 | 
			
		||||
                    $model["gradables"][] = $g->export_model();;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Add incoming and outgoing connection info
 | 
			
		||||
        // Add incoming and outgoing connection info.
 | 
			
		||||
        $conn_out = studyitemconnection::find_outgoing($this->id);
 | 
			
		||||
 | 
			
		||||
        if($mode == "export"){
 | 
			
		||||
            foreach($conn_out as $c) {
 | 
			
		||||
        if ($mode == "export") {
 | 
			
		||||
            foreach ($conn_out as $c) {
 | 
			
		||||
                $model["connections"][] = $c->to_id();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            foreach($conn_out as $c) {
 | 
			
		||||
            foreach ($conn_out as $c) {
 | 
			
		||||
                $model['connections']['out'][$c->to_id()] = $c->model();
 | 
			
		||||
            }
 | 
			
		||||
            $conn_in = studyitemconnection::find_incoming($this->id);
 | 
			
		||||
            foreach($conn_in as $c) {
 | 
			
		||||
            foreach ($conn_in as $c) {
 | 
			
		||||
                $model['connections']['in'][$c->from_id()] = $c->model();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -188,49 +208,47 @@ class studyitem {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function add($fields,$import=false)
 | 
			
		||||
    {
 | 
			
		||||
    public static function add($fields, $import=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $addable = ['line_id','type','layer','conditions','slot','competency_id','course_id','badge_id','continuation_id','span'];
 | 
			
		||||
        $addable = ['line_id', 'type', 'layer', 'conditions', 'slot', 'competency_id', 'course_id', 'badge_id', 'continuation_id', 'span'];
 | 
			
		||||
        $info = [ 'layer' => 0, ];
 | 
			
		||||
        foreach($addable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        foreach ($addable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $id = $DB->insert_record(self::TABLE, $info);
 | 
			
		||||
        $item = self::findById($id);
 | 
			
		||||
        if($item->type() == self::COURSE){
 | 
			
		||||
            // Signal the studyplan that a course has been added so it can be marked for csync cascading
 | 
			
		||||
        if ($item->type() == self::COURSE) {
 | 
			
		||||
            // Signal the studyplan that a course has been added so it can be marked for csync cascading.
 | 
			
		||||
            $item->studyline()->studyplan()->mark_csync_changed();
 | 
			
		||||
        }
 | 
			
		||||
        return $item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function edit($fields)
 | 
			
		||||
    {
 | 
			
		||||
    public function edit($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $editable = ['conditions','course_id','continuation_id','span'];
 | 
			
		||||
        $editable = ['conditions', 'course_id', 'continuation_id', 'span'];
 | 
			
		||||
 | 
			
		||||
        $info = ['id' => $this->id,];
 | 
			
		||||
        foreach($editable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields) && isset($fields[$f])){
 | 
			
		||||
        $info = ['id' => $this->id, ];
 | 
			
		||||
        foreach ($editable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields) && isset($fields[$f])) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $DB->update_record(self::TABLE, $info);
 | 
			
		||||
        //reload record after edit
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $this->id],"*",MUST_EXIST);
 | 
			
		||||
        //reload record after edit.
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function isValid(){
 | 
			
		||||
        // Check if referenced courses, badges and/or competencies still exist
 | 
			
		||||
        if($this->r->type == static::COURSE){
 | 
			
		||||
    public function isValid() {
 | 
			
		||||
        // Check if referenced courses, badges and/or competencies still exist.
 | 
			
		||||
        if ($this->r->type == static::COURSE) {
 | 
			
		||||
            return courseinfo::exists($this->r->course_id);
 | 
			
		||||
        }
 | 
			
		||||
        else if($this->r->type == static::BADGE){
 | 
			
		||||
        else if ($this->r->type == static::BADGE) {
 | 
			
		||||
            return badgeinfo::exists($this->r->badge_id);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -238,30 +256,29 @@ class studyitem {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($force=false)
 | 
			
		||||
    {
 | 
			
		||||
    public function delete($force=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        // check if this item is referenced in a START item
 | 
			
		||||
        if($force){
 | 
			
		||||
            // clear continuation id from any references to this item
 | 
			
		||||
            $records = $DB->get_records(self::TABLE,['continuation_id' => $this->id]);
 | 
			
		||||
            foreach($records as $r){
 | 
			
		||||
        // check if this item is referenced in a START item.
 | 
			
		||||
        if ($force) {
 | 
			
		||||
            // clear continuation id from any references to this item.
 | 
			
		||||
            $records = $DB->get_records(self::TABLE, ['continuation_id' => $this->id]);
 | 
			
		||||
            foreach ($records as $r) {
 | 
			
		||||
                $r->continuation_id = 0;
 | 
			
		||||
                $DB->update_record(self::TABLE,$r);
 | 
			
		||||
                $DB->update_record(self::TABLE, $r);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($DB->count_records(self::TABLE,['continuation_id' => $this->id]) > 0){
 | 
			
		||||
        if ($DB->count_records(self::TABLE, ['continuation_id' => $this->id]) > 0) {
 | 
			
		||||
            return success::fail('Cannot remove: item is referenced by another item');
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // delete al related connections to this item
 | 
			
		||||
            // delete al related connections to this item.
 | 
			
		||||
            studyitemconnection::clear($this->id);
 | 
			
		||||
            // delete all grade inclusion references to this item
 | 
			
		||||
            $DB->delete_records("local_treestudyplan_gradeinc",['studyitem_id' => $this->id]);
 | 
			
		||||
            // delete the item itself
 | 
			
		||||
            // delete all grade inclusion references to this item.
 | 
			
		||||
            $DB->delete_records("local_treestudyplan_gradeinc", ['studyitem_id' => $this->id]);
 | 
			
		||||
            // delete the item itself.
 | 
			
		||||
            $DB->delete_records(self::TABLE, ['id' => $this->id]);
 | 
			
		||||
 | 
			
		||||
            return success::success();
 | 
			
		||||
| 
						 | 
				
			
			@ -274,12 +291,10 @@ class studyitem {
 | 
			
		|||
     *                      *
 | 
			
		||||
     ************************/
 | 
			
		||||
 | 
			
		||||
    public static function reorder($resequence)
 | 
			
		||||
    {
 | 
			
		||||
    public static function reorder($resequence) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        foreach($resequence as $sq)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($resequence as $sq) {
 | 
			
		||||
            $DB->update_record(self::TABLE, [
 | 
			
		||||
                'id' => $sq['id'],
 | 
			
		||||
                'line_id' => $sq['line_id'],
 | 
			
		||||
| 
						 | 
				
			
			@ -291,19 +306,18 @@ class studyitem {
 | 
			
		|||
        return success::success();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function find_studyline_children(studyline $line)
 | 
			
		||||
    {
 | 
			
		||||
    public static function find_studyline_children(studyline $line) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE,"id","line_id = :line_id ORDER BY layer",['line_id' => $line->id()]);
 | 
			
		||||
        foreach($ids as $id) {
 | 
			
		||||
            $item = self::findById($id,$line);
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE, "id", "line_id = :line_id ORDER BY layer", ['line_id' => $line->id()]);
 | 
			
		||||
        foreach ($ids as $id) {
 | 
			
		||||
            $item = self::findById($id, $line);
 | 
			
		||||
            $list[] = $item;
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static function link_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    private static function link_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of study item'),
 | 
			
		||||
            "type" => new \external_value(PARAM_TEXT, 'type of study item'),
 | 
			
		||||
| 
						 | 
				
			
			@ -313,10 +327,10 @@ class studyitem {
 | 
			
		|||
        ], 'basic info of referenced studyitem', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function link_model($userid){
 | 
			
		||||
    private function link_model($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $line = $DB->get_record(studyline::TABLE,['id' => $this->r->line_id]);
 | 
			
		||||
        $plan = $DB->get_record(studyplan::TABLE,['id' => $line->studyplan_id]);
 | 
			
		||||
        $line = $DB->get_record(studyline::TABLE, ['id' => $this->r->line_id]);
 | 
			
		||||
        $plan = $DB->get_record(studyplan::TABLE, ['id' => $line->studyplan_id]);
 | 
			
		||||
 | 
			
		||||
        return  [
 | 
			
		||||
            "id" => $this->r->id,
 | 
			
		||||
| 
						 | 
				
			
			@ -327,7 +341,7 @@ class studyitem {
 | 
			
		|||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id"            => new \external_value(PARAM_INT, 'id of study item'),
 | 
			
		||||
            "type"          => new \external_value(PARAM_TEXT, 'type of study item'),
 | 
			
		||||
| 
						 | 
				
			
			@ -342,11 +356,11 @@ class studyitem {
 | 
			
		|||
                'in' => new \external_multiple_structure(studyitemconnection::structure()),
 | 
			
		||||
                'out' => new \external_multiple_structure(studyitemconnection::structure()),
 | 
			
		||||
            ]),
 | 
			
		||||
        ],'Study item info',$value);
 | 
			
		||||
        ], 'Study item info', $value);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid){
 | 
			
		||||
    public function user_model($userid) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
| 
						 | 
				
			
			@ -362,35 +376,32 @@ class studyitem {
 | 
			
		|||
            ]
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // Add badge info if available
 | 
			
		||||
        if(badgeinfo::exists($this->r->badge_id))
 | 
			
		||||
        {
 | 
			
		||||
        // Add badge info if available.
 | 
			
		||||
        if (badgeinfo::exists($this->r->badge_id)) {
 | 
			
		||||
            $badge = new \core_badges\badge($this->r->badge_id);
 | 
			
		||||
            $badgeinfo = new badgeinfo($badge);
 | 
			
		||||
            $model['badge'] = $badgeinfo->user_model($userid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add continuation_info if available
 | 
			
		||||
        if(self::exists($this->r->continuation_id))
 | 
			
		||||
        {
 | 
			
		||||
        // Add continuation_info if available.
 | 
			
		||||
        if (self::exists($this->r->continuation_id)) {
 | 
			
		||||
            $c_item = self::findById($this->r->continuation_id);
 | 
			
		||||
            $model['continuation'] = $c_item->link_model($userid);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add course if available
 | 
			
		||||
        if(courseinfo::exists($this->r->course_id))
 | 
			
		||||
        {
 | 
			
		||||
        // Add course if available.
 | 
			
		||||
        if (courseinfo::exists($this->r->course_id)) {
 | 
			
		||||
            $cinfo = $this->getcourseinfo();
 | 
			
		||||
            $model['course'] = $cinfo->user_model($userid,$this->aggregator->usecorecompletioninfo());
 | 
			
		||||
            $model['course'] = $cinfo->user_model($userid, $this->aggregator->usecorecompletioninfo());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add incoming and outgoing connection info
 | 
			
		||||
        // Add incoming and outgoing connection info.
 | 
			
		||||
        $conn_out = studyitemconnection::find_outgoing($this->id);
 | 
			
		||||
        foreach($conn_out as $c) {
 | 
			
		||||
        foreach ($conn_out as $c) {
 | 
			
		||||
            $model['connections']['out'][$c->to_id()] = $c->model();
 | 
			
		||||
        }
 | 
			
		||||
        $conn_in = studyitemconnection::find_incoming($this->id);
 | 
			
		||||
        foreach($conn_in as $c) {
 | 
			
		||||
        foreach ($conn_in as $c) {
 | 
			
		||||
            $model['connections']['in'][$c->from_id()] = $c->model();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -398,9 +409,8 @@ class studyitem {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getcourseinfo()
 | 
			
		||||
    {
 | 
			
		||||
        if(empty($this->courseinfo) && courseinfo::exists($this->r->course_id)){
 | 
			
		||||
    public function getcourseinfo() {
 | 
			
		||||
        if (empty($this->courseinfo) && courseinfo::exists($this->r->course_id)) {
 | 
			
		||||
            $this->courseinfo = new courseinfo($this->r->course_id, $this);
 | 
			
		||||
        }
 | 
			
		||||
        return $this->courseinfo;
 | 
			
		||||
| 
						 | 
				
			
			@ -409,47 +419,46 @@ class studyitem {
 | 
			
		|||
    private function completion($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if($this->isValid()){
 | 
			
		||||
            if(strtolower($this->r->type) == 'course'){
 | 
			
		||||
                // determine competency by competency completion
 | 
			
		||||
        if ($this->isValid()) {
 | 
			
		||||
            if (strtolower($this->r->type) == 'course') {
 | 
			
		||||
                // determine competency by competency completion.
 | 
			
		||||
                $courseinfo = $this->getcourseinfo();
 | 
			
		||||
                return $this->aggregator->aggregate_course($courseinfo,$this,$userid);
 | 
			
		||||
                return $this->aggregator->aggregate_course($courseinfo, $this, $userid);
 | 
			
		||||
            }
 | 
			
		||||
            elseif(strtolower($this->r->type) =='start'){
 | 
			
		||||
            else if (strtolower($this->r->type) =='start') {
 | 
			
		||||
                // Does not need to use aggregator.
 | 
			
		||||
                // Either true, or the completion of the reference
 | 
			
		||||
                if(self::exists($this->r->continuation_id)){
 | 
			
		||||
                // Either true, or the completion of the reference.
 | 
			
		||||
                if (self::exists($this->r->continuation_id)) {
 | 
			
		||||
                    $c_item = self::findById($this->r->continuation_id);
 | 
			
		||||
                    return $c_item->completion($userid);
 | 
			
		||||
                } else {
 | 
			
		||||
                    return completion::COMPLETED;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            elseif(in_array(strtolower($this->r->type),['junction','finish'])){
 | 
			
		||||
                // completion of the linked items, according to the rule
 | 
			
		||||
            else if (in_array(strtolower($this->r->type), ['junction', 'finish'])) {
 | 
			
		||||
                // completion of the linked items, according to the rule.
 | 
			
		||||
                $in_completed = [];
 | 
			
		||||
                // Retrieve incoming connections
 | 
			
		||||
                $incoming = $DB->get_records(studyitemconnection::TABLE,['to_id' => $this->r->id]);
 | 
			
		||||
                foreach($incoming as $conn){
 | 
			
		||||
                // Retrieve incoming connections.
 | 
			
		||||
                $incoming = $DB->get_records(studyitemconnection::TABLE, ['to_id' => $this->r->id]);
 | 
			
		||||
                foreach ($incoming as $conn) {
 | 
			
		||||
                    $item = self::findById($conn->from_id);
 | 
			
		||||
                    $in_completed[] = $item->completion($userid);
 | 
			
		||||
                }
 | 
			
		||||
                return $this->aggregator->aggregate_junction($in_completed,$this,$userid);
 | 
			
		||||
                return $this->aggregator->aggregate_junction($in_completed, $this, $userid);
 | 
			
		||||
            }
 | 
			
		||||
            elseif(strtolower($this->r->type) =='badge'){
 | 
			
		||||
            else if (strtolower($this->r->type) =='badge') {
 | 
			
		||||
                global $DB;
 | 
			
		||||
                // badge awarded
 | 
			
		||||
                if(badgeinfo::exists($this->r->badge_id))
 | 
			
		||||
                {
 | 
			
		||||
                // badge awarded.
 | 
			
		||||
                if (badgeinfo::exists($this->r->badge_id)) {
 | 
			
		||||
                    $badge = new \core_badges\badge($this->r->badge_id);
 | 
			
		||||
                    if($badge->is_issued($userid)){
 | 
			
		||||
                        if($badge->can_expire()){
 | 
			
		||||
                            // get the issued badges and check if any of them have not expired yet
 | 
			
		||||
                            $badges_issued = $DB->get_records("badge_issued",["badge_id" => $this->r->badge_id, "user_id" => $userid]);
 | 
			
		||||
                    if ($badge->is_issued($userid)) {
 | 
			
		||||
                        if ($badge->can_expire()) {
 | 
			
		||||
                            // get the issued badges and check if any of them have not expired yet.
 | 
			
		||||
                            $badges_issued = $DB->get_records("badge_issued", ["badge_id" => $this->r->badge_id, "user_id" => $userid]);
 | 
			
		||||
                            $notexpired = false;
 | 
			
		||||
                            $now = time();
 | 
			
		||||
                            foreach($badges_issued as $bi){
 | 
			
		||||
                                if($bi->dateexpire == null || $bi->dateexpire > $now){
 | 
			
		||||
                            foreach ($badges_issued as $bi) {
 | 
			
		||||
                                if ($bi->dateexpire == null || $bi->dateexpire > $now) {
 | 
			
		||||
                                    $notexpired = true;
 | 
			
		||||
                                    break;
 | 
			
		||||
                                }
 | 
			
		||||
| 
						 | 
				
			
			@ -467,57 +476,57 @@ class studyitem {
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // return incomplete for other types
 | 
			
		||||
                // return incomplete for other types.
 | 
			
		||||
                return completion::INCOMPLETE;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // return incomplete for other types
 | 
			
		||||
            // return incomplete for other types.
 | 
			
		||||
            return completion::INCOMPLETE;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function duplicate($new_line){
 | 
			
		||||
    public function duplicate($new_line) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        // clone the database fields
 | 
			
		||||
        // clone the database fields.
 | 
			
		||||
        $fields = clone $this->r;
 | 
			
		||||
        // set new line id
 | 
			
		||||
        // set new line id.
 | 
			
		||||
        unset($fields->id);
 | 
			
		||||
        $fields->line_id = $new_line->id();
 | 
			
		||||
        //create new record with the new data
 | 
			
		||||
        //create new record with the new data.
 | 
			
		||||
        $id = $DB->insert_record(self::TABLE, (array)$fields);
 | 
			
		||||
        $new = self::findById($id,$new_line);
 | 
			
		||||
        $new = self::findById($id, $new_line);
 | 
			
		||||
 | 
			
		||||
        // copy the grading info if relevant
 | 
			
		||||
        // copy the grading info if relevant.
 | 
			
		||||
        $gradables = gradeinfo::list_studyitem_gradables($this);
 | 
			
		||||
        foreach($gradables as $g){
 | 
			
		||||
            gradeinfo::include_grade($g->getGradeitem()->id,$new,true);
 | 
			
		||||
        foreach ($gradables as $g) {
 | 
			
		||||
            gradeinfo::include_grade($g->getGradeitem()->id, $new->id(), true);
 | 
			
		||||
        }
 | 
			
		||||
        return $new;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_model(){
 | 
			
		||||
    public function export_model() {
 | 
			
		||||
        return $this->generate_model("export");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function import_item($model){
 | 
			
		||||
    public static function import_item($model) {
 | 
			
		||||
        unset($model["course_id"]);
 | 
			
		||||
        unset($model["competency_id"]);
 | 
			
		||||
        unset($model["badge_id"]);
 | 
			
		||||
        unset($model["continuation_id"]);
 | 
			
		||||
        if(isset($model["course"])){
 | 
			
		||||
        if (isset($model["course"])) {
 | 
			
		||||
            $model["course_id"] = courseinfo::id_from_shortname(($model["course"]));
 | 
			
		||||
        }
 | 
			
		||||
        if(isset($model["badge"])){
 | 
			
		||||
        if (isset($model["badge"])) {
 | 
			
		||||
            $model["badge_id"] = badgeinfo::id_from_name(($model["badge"]));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $item = self::add($model,true);
 | 
			
		||||
        $item = self::add($model, true);
 | 
			
		||||
 | 
			
		||||
        if(isset($model["course_id"])){
 | 
			
		||||
            // attempt to import the gradables
 | 
			
		||||
            foreach($model["gradables"] as $gradable){
 | 
			
		||||
                gradeinfo::import($item,$gradable);
 | 
			
		||||
        if (isset($model["course_id"])) {
 | 
			
		||||
            // attempt to import the gradables.
 | 
			
		||||
            foreach ($model["gradables"] as $gradable) {
 | 
			
		||||
                gradeinfo::import($item, $gradable);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,86 +31,82 @@ class studyitemconnection {
 | 
			
		|||
    private $id;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected function __construct($r){
 | 
			
		||||
    protected function __construct($r) {
 | 
			
		||||
        $this->r = $r;
 | 
			
		||||
        $this->id = $r->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            'id' => new \external_value(PARAM_INT, 'id of connection'),
 | 
			
		||||
            'from_id' => new \external_value(PARAM_INT, 'id of start item'),
 | 
			
		||||
            'to_id' => new \external_value(PARAM_INT, 'id of end item'),
 | 
			
		||||
        ],'',$value);
 | 
			
		||||
        ], '', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function model(){
 | 
			
		||||
    public function model() {
 | 
			
		||||
        return ['id' => $this->r->id, 'from_id' => $this->r->from_id, 'to_id' => $this->r->to_id];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function from_item(){
 | 
			
		||||
    public function from_item() {
 | 
			
		||||
        return studyitem::findById($this->r->from_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function to_item(){
 | 
			
		||||
    public function to_item() {
 | 
			
		||||
        return studyitem::findById($this->r->to_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function from_id(){
 | 
			
		||||
    public function from_id() {
 | 
			
		||||
        return $this->r->from_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function to_id(){
 | 
			
		||||
    public function to_id() {
 | 
			
		||||
        return $this->r->to_id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static function find_outgoing($item_id){
 | 
			
		||||
    public static function find_outgoing($item_id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $conn_out = $DB->get_records(self::TABLE,['from_id' => $item_id]);
 | 
			
		||||
        foreach($conn_out as $c) {
 | 
			
		||||
        $conn_out = $DB->get_records(self::TABLE, ['from_id' => $item_id]);
 | 
			
		||||
        foreach ($conn_out as $c) {
 | 
			
		||||
            $list[] = new self($c);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function find_incoming($item_id){
 | 
			
		||||
    public static function find_incoming($item_id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $conn_in = $DB->get_records(self::TABLE,['to_id' => $item_id]);
 | 
			
		||||
        foreach($conn_in as $c) {
 | 
			
		||||
        $conn_in = $DB->get_records(self::TABLE, ['to_id' => $item_id]);
 | 
			
		||||
        foreach ($conn_in as $c) {
 | 
			
		||||
            $list[] = new self($c);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function connect($from_id,$to_id)
 | 
			
		||||
    {
 | 
			
		||||
    public static function connect($from_id, $to_id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        //check if link already exists
 | 
			
		||||
        //check if link already exists.
 | 
			
		||||
 | 
			
		||||
        if(!$DB->record_exists(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id]))
 | 
			
		||||
        {
 | 
			
		||||
        if (!$DB->record_exists(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id])) {
 | 
			
		||||
            $id = $DB->insert_record(self::TABLE, [
 | 
			
		||||
                'from_id' => $from_id,
 | 
			
		||||
                'to_id' => $to_id,
 | 
			
		||||
            ]);
 | 
			
		||||
 | 
			
		||||
            return new self($DB->get_record(self::TABLE,['id' => $id]));
 | 
			
		||||
            return new self($DB->get_record(self::TABLE, ['id' => $id]));
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            return new self($DB->get_record(self::TABLE,['from_id' => $from_id, 'to_id' => $to_id]));
 | 
			
		||||
            return new self($DB->get_record(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id]));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function disconnect($from_id,$to_id)
 | 
			
		||||
    {
 | 
			
		||||
    public static function disconnect($from_id, $to_id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if($DB->record_exists(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id]))
 | 
			
		||||
        {
 | 
			
		||||
        if ($DB->record_exists(self::TABLE, ['from_id' => $from_id, 'to_id' => $to_id])) {
 | 
			
		||||
            $DB->delete_records(self::TABLE, [
 | 
			
		||||
                'from_id' => $from_id,
 | 
			
		||||
                'to_id' => $to_id,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +45,7 @@ class studyline {
 | 
			
		|||
 | 
			
		||||
    private static $STUDYLINE_CACHE = [];
 | 
			
		||||
 | 
			
		||||
    private $r; // Holds database record
 | 
			
		||||
    private $r; // Holds database record.
 | 
			
		||||
    private $id;
 | 
			
		||||
    private $page;
 | 
			
		||||
    private $studyplan;
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +63,7 @@ class studyline {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    public static function findById($id): self {
 | 
			
		||||
        if(!array_key_exists($id,self::$STUDYLINE_CACHE)){
 | 
			
		||||
        if (!array_key_exists($id, self::$STUDYLINE_CACHE)) {
 | 
			
		||||
            self::$STUDYLINE_CACHE[$id] = new self($id);
 | 
			
		||||
        }
 | 
			
		||||
        return self::$STUDYLINE_CACHE[$id];
 | 
			
		||||
| 
						 | 
				
			
			@ -51,23 +72,23 @@ class studyline {
 | 
			
		|||
    private function __construct($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $id]);
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $id]);
 | 
			
		||||
        $this->page = studyplanpage::findById($this->r->page_id);
 | 
			
		||||
        $this->studyplan = $this->page->studyplan();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function name(){
 | 
			
		||||
    public function name() {
 | 
			
		||||
        return $this->r->name;
 | 
			
		||||
    }
 | 
			
		||||
    public function shortname(){
 | 
			
		||||
    public function shortname() {
 | 
			
		||||
        return $this->r->shortname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyline'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'shortname of studyline'),
 | 
			
		||||
| 
						 | 
				
			
			@ -76,20 +97,20 @@ class studyline {
 | 
			
		|||
            "sequence" => new \external_value(PARAM_INT, 'order of studyline'),
 | 
			
		||||
            "slots" => new \external_multiple_structure(
 | 
			
		||||
                new \external_single_structure([
 | 
			
		||||
                    self::SLOTSET_COMPETENCY => new \external_multiple_structure(studyitem::editor_structure(),'competency items',VALUE_OPTIONAL),
 | 
			
		||||
                    self::SLOTSET_FILTER => new \external_multiple_structure(studyitem::editor_structure(),'filter items'),
 | 
			
		||||
                    self::SLOTSET_COMPETENCY => new \external_multiple_structure(studyitem::editor_structure(), 'competency items', VALUE_OPTIONAL),
 | 
			
		||||
                    self::SLOTSET_FILTER => new \external_multiple_structure(studyitem::editor_structure(), 'filter items'),
 | 
			
		||||
                ])
 | 
			
		||||
            )
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(){
 | 
			
		||||
    public function editor_model() {
 | 
			
		||||
        return $this->generate_model("editor");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function generate_model($mode){
 | 
			
		||||
        // Mode parameter is used to geep this function for both editor model and export model
 | 
			
		||||
        // (Export model results in fewer parameters on children, but is otherwise basically the same as this function)
 | 
			
		||||
    protected function generate_model($mode) {
 | 
			
		||||
        // Mode parameter is used to geep this function for both editor model and export model.
 | 
			
		||||
        // (Export model results in fewer parameters on children, but is otherwise basically the same as this function).
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
| 
						 | 
				
			
			@ -100,27 +121,27 @@ class studyline {
 | 
			
		|||
            'sequence' => $this->r->sequence,
 | 
			
		||||
            'slots' => [],
 | 
			
		||||
        ];
 | 
			
		||||
        if($mode == "export"){
 | 
			
		||||
            // Id and sequence are not used in export model
 | 
			
		||||
        if ($mode == "export") {
 | 
			
		||||
            // Id and sequence are not used in export model.
 | 
			
		||||
            unset($model["id"]);
 | 
			
		||||
            unset($model["sequence"]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO: Make this a little nicer
 | 
			
		||||
        // Get the number of slots
 | 
			
		||||
        // As a safety data integrity measure, if there are any items in a higher slot than currently allowed, 
 | 
			
		||||
        // make sure there are enought slots to account for them
 | 
			
		||||
        // TODO: Make this a little nicer.
 | 
			
		||||
        // Get the number of slots.
 | 
			
		||||
        // As a safety data integrity measure, if there are any items in a higher slot than currently allowed, .
 | 
			
		||||
        // make sure there are enought slots to account for them.
 | 
			
		||||
        // Alternatively, we could ensure that on reduction of slots, the items that no longer have a slot will be removed.
 | 
			
		||||
        $max_slot = $DB->get_field_select(studyitem::TABLE,"MAX(slot)","line_id = :lineid",['lineid' => $this->id]);
 | 
			
		||||
        $num_slots = max($this->page->periods(),$max_slot +1);
 | 
			
		||||
        $max_slot = $DB->get_field_select(studyitem::TABLE, "MAX(slot)", "line_id = :lineid", ['lineid' => $this->id]);
 | 
			
		||||
        $num_slots = max($this->page->periods(), $max_slot +1);
 | 
			
		||||
 | 
			
		||||
        // Create the required amount of slots
 | 
			
		||||
        for($i=0; $i < $num_slots+1; $i++){
 | 
			
		||||
            if($mode == "export") {
 | 
			
		||||
                // Export mode does not separate between filter or competency type, since that is determined automatically
 | 
			
		||||
        // Create the required amount of slots.
 | 
			
		||||
        for($i=0; $i < $num_slots+1; $i++) {
 | 
			
		||||
            if ($mode == "export") {
 | 
			
		||||
                // Export mode does not separate between filter or competency type, since that is determined automatically.
 | 
			
		||||
                $slots = [];
 | 
			
		||||
            } else {
 | 
			
		||||
                if($i > 0) {
 | 
			
		||||
                if ($i > 0) {
 | 
			
		||||
                    $slots = [self::SLOTSET_COMPETENCY => [], self::SLOTSET_FILTER => []];
 | 
			
		||||
                } else {
 | 
			
		||||
                    $slots = [self::SLOTSET_FILTER => []];
 | 
			
		||||
| 
						 | 
				
			
			@ -130,23 +151,22 @@ class studyline {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        $children = studyitem::find_studyline_children($this);
 | 
			
		||||
        foreach($children as $c)
 | 
			
		||||
        {
 | 
			
		||||
            if($mode == "export") {
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            if ($mode == "export") {
 | 
			
		||||
                $model['slots'][$c->slot()][] = $c->export_model();
 | 
			
		||||
            } else {
 | 
			
		||||
                $slotset = null;
 | 
			
		||||
                if($c->slot() > 0) {
 | 
			
		||||
                    if(in_array($c->type(),self::COMPETENCY_TYPES)) {
 | 
			
		||||
                if ($c->slot() > 0) {
 | 
			
		||||
                    if (in_array($c->type(), self::COMPETENCY_TYPES)) {
 | 
			
		||||
                        $slotset = self::SLOTSET_COMPETENCY;
 | 
			
		||||
                    } else if(in_array($c->type(),self::FILTER_TYPES)) {
 | 
			
		||||
                    } else if (in_array($c->type(), self::FILTER_TYPES)) {
 | 
			
		||||
                        $slotset = self::SLOTSET_FILTER;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if(in_array($c->type(),self::FILTER0_TYPES)) {
 | 
			
		||||
                else if (in_array($c->type(), self::FILTER0_TYPES)) {
 | 
			
		||||
                    $slotset = self::SLOTSET_FILTER;
 | 
			
		||||
                }
 | 
			
		||||
                if(isset($slotset)) {
 | 
			
		||||
                if (isset($slotset)) {
 | 
			
		||||
                    $model['slots'][$c->slot()][$slotset][] = $c->editor_model();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -154,19 +174,19 @@ class studyline {
 | 
			
		|||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function add($fields){
 | 
			
		||||
    public static function add($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if(!isset($fields['page_id'])){
 | 
			
		||||
        if (!isset($fields['page_id'])) {
 | 
			
		||||
            throw new \InvalidArgumentException("parameter 'page_id' missing");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $page_id = $fields['page_id'];
 | 
			
		||||
        $sqmax = $DB->get_field_select(self::TABLE,"MAX(sequence)","page_id = :page_id",['page_id' => $page_id]);
 | 
			
		||||
        $addable = ['page_id','name','shortname','color'];
 | 
			
		||||
        $sqmax = $DB->get_field_select(self::TABLE, "MAX(sequence)", "page_id = :page_id", ['page_id' => $page_id]);
 | 
			
		||||
        $addable = ['page_id', 'name', 'shortname', 'color'];
 | 
			
		||||
        $info = ['sequence' => $sqmax+1];
 | 
			
		||||
        foreach($addable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        foreach ($addable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -174,32 +194,32 @@ class studyline {
 | 
			
		|||
        return self::findById($id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function edit($fields){
 | 
			
		||||
    public function edit($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $editable = ['name','shortname','color'];
 | 
			
		||||
        $info = ['id' => $this->id,];
 | 
			
		||||
        foreach($editable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        $editable = ['name', 'shortname', 'color'];
 | 
			
		||||
        $info = ['id' => $this->id, ];
 | 
			
		||||
        foreach ($editable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $DB->update_record(self::TABLE, $info);
 | 
			
		||||
        //reload record after edit
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $this->id],"*",MUST_EXIST);
 | 
			
		||||
        //reload record after edit.
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST);
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($force = false){
 | 
			
		||||
    public function delete($force = false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if($force){
 | 
			
		||||
        if ($force) {
 | 
			
		||||
            $children = studyitem::find_studyline_children($this);
 | 
			
		||||
            foreach($children as $c){
 | 
			
		||||
            foreach ($children as $c) {
 | 
			
		||||
                $c->delete($force);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // check if this item has study items in it
 | 
			
		||||
        if($DB->count_records(studyitem::TABLE,['line_id' => $this->id]) > 0){
 | 
			
		||||
        // check if this item has study items in it.
 | 
			
		||||
        if ($DB->count_records(studyitem::TABLE, ['line_id' => $this->id]) > 0) {
 | 
			
		||||
            return success::fail('cannot delete studyline with items');
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
| 
						 | 
				
			
			@ -209,12 +229,10 @@ class studyline {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function reorder($resequence)
 | 
			
		||||
    {
 | 
			
		||||
    public static function reorder($resequence) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        foreach($resequence as $sq)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($resequence as $sq) {
 | 
			
		||||
            $DB->update_record(self::TABLE, [
 | 
			
		||||
                'id' => $sq['id'],
 | 
			
		||||
                'sequence' => $sq['sequence'],
 | 
			
		||||
| 
						 | 
				
			
			@ -224,19 +242,18 @@ class studyline {
 | 
			
		|||
        return success::success();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function find_page_children(studyplanpage $page)
 | 
			
		||||
    {
 | 
			
		||||
    public static function find_page_children(studyplanpage $page) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE,"id","page_id = :page_id ORDER BY sequence",
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE, "id", "page_id = :page_id ORDER BY sequence",
 | 
			
		||||
                                        ['page_id' => $page->id()]);
 | 
			
		||||
        foreach($ids as $id) {
 | 
			
		||||
        foreach ($ids as $id) {
 | 
			
		||||
            $list[] = self::findById($id);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyline'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'shortname of studyline'),
 | 
			
		||||
| 
						 | 
				
			
			@ -245,15 +262,15 @@ class studyline {
 | 
			
		|||
            "sequence" => new \external_value(PARAM_INT, 'order of studyline'),
 | 
			
		||||
            "slots" => new \external_multiple_structure(
 | 
			
		||||
                new \external_single_structure([
 | 
			
		||||
                    self::SLOTSET_COMPETENCY => new \external_multiple_structure(studyitem::user_structure(),'competency items',VALUE_OPTIONAL),
 | 
			
		||||
                    self::SLOTSET_FILTER => new \external_multiple_structure(studyitem::user_structure(),'filter items'),
 | 
			
		||||
                    self::SLOTSET_COMPETENCY => new \external_multiple_structure(studyitem::user_structure(), 'competency items', VALUE_OPTIONAL),
 | 
			
		||||
                    self::SLOTSET_FILTER => new \external_multiple_structure(studyitem::user_structure(), 'filter items'),
 | 
			
		||||
                ])
 | 
			
		||||
            )
 | 
			
		||||
        ],'Studyline with user info',$value);
 | 
			
		||||
        ], 'Studyline with user info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid){
 | 
			
		||||
        // TODO: Integrate this function into generate_model() for ease of maintenance
 | 
			
		||||
    public function user_model($userid) {
 | 
			
		||||
        // TODO: Integrate this function into generate_model() for ease of maintenance.
 | 
			
		||||
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -266,16 +283,16 @@ class studyline {
 | 
			
		|||
            'slots' => [],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        // Get the number of slots
 | 
			
		||||
        // As a safety data integrity measure, if there are any items in a higher slot than currently allowed, 
 | 
			
		||||
        // make sure there are enought slots to account for them
 | 
			
		||||
        // Get the number of slots.
 | 
			
		||||
        // As a safety data integrity measure, if there are any items in a higher slot than currently allowed, .
 | 
			
		||||
        // make sure there are enought slots to account for them.
 | 
			
		||||
        // Alternatively, we could ensure that on reduction of slots, the items that no longer have a slot will be removed.
 | 
			
		||||
        $max_slot = $DB->get_field_select(studyitem::TABLE,"MAX(slot)","line_id = :lineid",['lineid' => $this->id]);
 | 
			
		||||
        $num_slots = max($this->page->periods(),$max_slot +1);
 | 
			
		||||
        $max_slot = $DB->get_field_select(studyitem::TABLE, "MAX(slot)", "line_id = :lineid", ['lineid' => $this->id]);
 | 
			
		||||
        $num_slots = max($this->page->periods(), $max_slot +1);
 | 
			
		||||
 | 
			
		||||
        // Create the required amount of slots
 | 
			
		||||
        for($i=0; $i < $num_slots+1; $i++){
 | 
			
		||||
            if($i > 0) {
 | 
			
		||||
        // Create the required amount of slots.
 | 
			
		||||
        for($i=0; $i < $num_slots+1; $i++) {
 | 
			
		||||
            if ($i > 0) {
 | 
			
		||||
                $slots = [self::SLOTSET_COMPETENCY => [], self::SLOTSET_FILTER => []];
 | 
			
		||||
            } else {
 | 
			
		||||
                $slots = [self::SLOTSET_FILTER => []];
 | 
			
		||||
| 
						 | 
				
			
			@ -284,21 +301,20 @@ class studyline {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        $children = studyitem::find_studyline_children($this);
 | 
			
		||||
        foreach($children as $c)
 | 
			
		||||
        {
 | 
			
		||||
            if($c->isValid()){
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            if ($c->isValid()) {
 | 
			
		||||
                $slotset = null;
 | 
			
		||||
                if($c->slot() > 0) {
 | 
			
		||||
                    if(in_array($c->type(),self::COMPETENCY_TYPES)) {
 | 
			
		||||
                if ($c->slot() > 0) {
 | 
			
		||||
                    if (in_array($c->type(), self::COMPETENCY_TYPES)) {
 | 
			
		||||
                        $slotset = self::SLOTSET_COMPETENCY;
 | 
			
		||||
                    } else if(in_array($c->type(),self::FILTER_TYPES)) {
 | 
			
		||||
                    } else if (in_array($c->type(), self::FILTER_TYPES)) {
 | 
			
		||||
                        $slotset = self::SLOTSET_FILTER;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if(in_array($c->type(),self::FILTER0_TYPES)) {
 | 
			
		||||
                else if (in_array($c->type(), self::FILTER0_TYPES)) {
 | 
			
		||||
                    $slotset = self::SLOTSET_FILTER;
 | 
			
		||||
                }
 | 
			
		||||
                if(isset($slotset)) {
 | 
			
		||||
                if (isset($slotset)) {
 | 
			
		||||
                    $model['slots'][$c->slot()][$slotset][] = $c->user_model($userid);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -309,45 +325,41 @@ class studyline {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function duplicate($new_studyplan,&$translation){
 | 
			
		||||
    public function duplicate($new_studyplan, &$translation) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        // clone the database fields
 | 
			
		||||
        // clone the database fields.
 | 
			
		||||
        $fields = clone $this->r;
 | 
			
		||||
        // set new studyplan id
 | 
			
		||||
        // set new studyplan id.
 | 
			
		||||
        unset($fields->id);
 | 
			
		||||
        $fields->studyplan_id = $new_studyplan->id();
 | 
			
		||||
        // create new record with the new data
 | 
			
		||||
        // create new record with the new data.
 | 
			
		||||
        $id = $DB->insert_record(self::TABLE, (array)$fields);
 | 
			
		||||
        $new = self::findById($id);
 | 
			
		||||
 | 
			
		||||
        // Next copy all the study items for this studyline
 | 
			
		||||
        // and record the original and copy id's in the $translation array
 | 
			
		||||
        // so the calling function can connect the new studyitems as required
 | 
			
		||||
        // Next copy all the study items for this studyline.
 | 
			
		||||
        // and record the original and copy id's in the $translation array.
 | 
			
		||||
        // so the calling function can connect the new studyitems as required.
 | 
			
		||||
        $children = studyitem::find_studyline_children($this);
 | 
			
		||||
        $translation = [];
 | 
			
		||||
        foreach($children as $c)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            $newchild = $c->duplicate($new);
 | 
			
		||||
            $translation[$c->id()] = $newchild->id();
 | 
			
		||||
        }
 | 
			
		||||
        return $new;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_model()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_model() {
 | 
			
		||||
        return $this->generate_model("export");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function import_studyitems($model,&$itemtranslation,&$connections){
 | 
			
		||||
    public function import_studyitems($model, &$itemtranslation, &$connections) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        foreach($model as $slot=>$slotmodel)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($model as $slot=>$slotmodel) {
 | 
			
		||||
            $courselayer = 0;
 | 
			
		||||
            $filterlayer = 0;
 | 
			
		||||
            foreach($slotmodel as $itemmodel)
 | 
			
		||||
            {
 | 
			
		||||
                if($itemmodel["type"] == "course"){
 | 
			
		||||
            foreach ($slotmodel as $itemmodel) {
 | 
			
		||||
                if ($itemmodel["type"] == "course") {
 | 
			
		||||
                    $itemmodel["layer"] = $courselayer;
 | 
			
		||||
                    $courselayer++;
 | 
			
		||||
                }else {
 | 
			
		||||
| 
						 | 
				
			
			@ -359,14 +371,14 @@ class studyline {
 | 
			
		|||
                $itemmodel["line_id"] = $this->id();
 | 
			
		||||
                $item = studyitem::import_item($itemmodel);
 | 
			
		||||
 | 
			
		||||
                if(!empty($item)){
 | 
			
		||||
                if (!empty($item)) {
 | 
			
		||||
                    $itemtranslation[$itemmodel["id"]] = $item->id();
 | 
			
		||||
 | 
			
		||||
                    if(count($itemmodel["connections"]) > 0){
 | 
			
		||||
                        if(! isset($connections[$item->id()]) || ! is_array($connections[$item->id()])){
 | 
			
		||||
                    if (count($itemmodel["connections"]) > 0) {
 | 
			
		||||
                        if (! isset($connections[$item->id()]) || ! is_array($connections[$item->id()])) {
 | 
			
		||||
                            $connections[$item->id()] = [];
 | 
			
		||||
                        }
 | 
			
		||||
                        foreach($itemmodel["connections"] as $to_id){
 | 
			
		||||
                        foreach ($itemmodel["connections"] as $to_id) {
 | 
			
		||||
                            $connections[$item->id()][] = $to_id;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -10,20 +31,20 @@ class studyplan {
 | 
			
		|||
    private static $STUDYPLAN_CACHE = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private $r; // Holds database record
 | 
			
		||||
    private $r; // Holds database record.
 | 
			
		||||
    private $id;
 | 
			
		||||
    private $aggregator;
 | 
			
		||||
    private $context = null; // Hold context object once retrieved
 | 
			
		||||
    private $linked_userids = null; // cache lookup of linked users (saves queries)
 | 
			
		||||
    private $context = null; // Hold context object once retrieved.
 | 
			
		||||
    private $linked_userids = null; // cache lookup of linked users (saves queries).
 | 
			
		||||
    private $page_cache = null;
 | 
			
		||||
 | 
			
		||||
    public function aggregator(){ 
 | 
			
		||||
    public function aggregator() {
 | 
			
		||||
        return $this->aggregator;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cache constructors to avoid multiple creation events in one session.
 | 
			
		||||
    public static function findById($id): self {
 | 
			
		||||
        if(!array_key_exists($id,self::$STUDYPLAN_CACHE)){
 | 
			
		||||
        if (!array_key_exists($id, self::$STUDYPLAN_CACHE)) {
 | 
			
		||||
            self::$STUDYPLAN_CACHE[$id] = new self($id);
 | 
			
		||||
        }
 | 
			
		||||
        return self::$STUDYPLAN_CACHE[$id];
 | 
			
		||||
| 
						 | 
				
			
			@ -32,28 +53,28 @@ class studyplan {
 | 
			
		|||
    private function __construct($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $id]);
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $id]);
 | 
			
		||||
 | 
			
		||||
        $this->aggregator = aggregator::createOrDefault($this->r->aggregation, $this->r->aggregation_config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function shortname(){
 | 
			
		||||
    public function shortname() {
 | 
			
		||||
        return $this->r->shortname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function name(){
 | 
			
		||||
    public function name() {
 | 
			
		||||
        return $this->r->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function pages(){
 | 
			
		||||
    public function pages() {
 | 
			
		||||
        // cached version of find_studyplan_children.
 | 
			
		||||
        // (may be premature optimization, since 
 | 
			
		||||
        // find_studyplan_children also does some caching)
 | 
			
		||||
        if(empty($this->page_cache)){
 | 
			
		||||
        // (may be premature optimization, since .
 | 
			
		||||
        // find_studyplan_children also does some caching).
 | 
			
		||||
        if (empty($this->page_cache)) {
 | 
			
		||||
            $this->page_cache = studyplanpage::find_studyplan_children($this);
 | 
			
		||||
        }
 | 
			
		||||
        return $this->page_cache;
 | 
			
		||||
| 
						 | 
				
			
			@ -63,18 +84,18 @@ class studyplan {
 | 
			
		|||
     * Return the context this studyplan is associated to
 | 
			
		||||
     */
 | 
			
		||||
    public function context(): \context{
 | 
			
		||||
        if(!isset($this->context)){
 | 
			
		||||
        if (!isset($this->context)) {
 | 
			
		||||
            try{
 | 
			
		||||
                $this->context = contextinfo::by_id($this->r->context_id)->context;
 | 
			
		||||
            }
 | 
			
		||||
            catch(\dml_missing_record_exception $x){
 | 
			
		||||
                throw new \InvalidArgumentException("Context {$this->r->context_id} not available"); // Just throw it up again. catch is included here to make sure we know it throws this exception
 | 
			
		||||
            catch(\dml_missing_record_exception $x) {
 | 
			
		||||
                throw new \InvalidArgumentException("Context {$this->r->context_id} not available"); // Just throw it up again. catch is included here to make sure we know it throws this exception.
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $this->context;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function simple_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function simple_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyplan'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
 | 
			
		||||
| 
						 | 
				
			
			@ -85,13 +106,13 @@ class studyplan {
 | 
			
		|||
            "aggregation" => new \external_value(PARAM_TEXT, 'selected aggregator'),
 | 
			
		||||
            "aggregation_config" => new \external_value(PARAM_TEXT, 'config string for aggregator'),
 | 
			
		||||
            "aggregation_info" => aggregator::basic_structure(),
 | 
			
		||||
            "pages" => new \external_multiple_structure(studyplanpage::simple_structure(),'pages'),
 | 
			
		||||
        ],'Basic studyplan info',$value);
 | 
			
		||||
            "pages" => new \external_multiple_structure(studyplanpage::simple_structure(), 'pages'),
 | 
			
		||||
        ], 'Basic studyplan info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function simple_model(){
 | 
			
		||||
    public function simple_model() {
 | 
			
		||||
        $pages = [];
 | 
			
		||||
        foreach($this->pages() as $p){
 | 
			
		||||
        foreach ($this->pages() as $p) {
 | 
			
		||||
            $pages[] = $p->simple_model();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +130,7 @@ class studyplan {
 | 
			
		|||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyplan'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
 | 
			
		||||
| 
						 | 
				
			
			@ -127,12 +148,12 @@ class studyplan {
 | 
			
		|||
                            "id" => new \external_value(PARAM_INT, 'id of scale'),
 | 
			
		||||
                            "name" => new \external_value(PARAM_TEXT, 'name of scale'),
 | 
			
		||||
                        ])),
 | 
			
		||||
                    ],"Scale forcing on stuff", VALUE_OPTIONAL), 
 | 
			
		||||
                ],"Advanced features available", VALUE_OPTIONAL),
 | 
			
		||||
            ],'Studyplan full structure',$value);
 | 
			
		||||
                    ], "Scale forcing on stuff", VALUE_OPTIONAL),
 | 
			
		||||
                ], "Advanced features available", VALUE_OPTIONAL),
 | 
			
		||||
            ], 'Studyplan full structure', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(){
 | 
			
		||||
    public function editor_model() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
| 
						 | 
				
			
			@ -148,21 +169,20 @@ class studyplan {
 | 
			
		|||
            'pages' => [],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        foreach($this->pages() as $p)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($this->pages() as $p) {
 | 
			
		||||
            $model['pages'][] = $p->editor_model();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if(has_capability('local/treestudyplan:forcescales', \context_system::instance())){
 | 
			
		||||
        if (has_capability('local/treestudyplan:forcescales', \context_system::instance())) {
 | 
			
		||||
 | 
			
		||||
            if(!array_key_exists('advanced',$model)){
 | 
			
		||||
                // Create advanced node if it does not exist
 | 
			
		||||
            if (!array_key_exists('advanced', $model)) {
 | 
			
		||||
                // Create advanced node if it does not exist.
 | 
			
		||||
                $model['advanced'] = [];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // get a list of available scales
 | 
			
		||||
            $scales = array_map( function($scale){
 | 
			
		||||
                return [ "id" => $scale->id, "name" => $scale->name,];
 | 
			
		||||
            // get a list of available scales.
 | 
			
		||||
            $scales = array_map( function($scale) {
 | 
			
		||||
                return [ "id" => $scale->id, "name" => $scale->name, ];
 | 
			
		||||
                }, \grade_scale::fetch_all(array('courseid'=>0)) ) ;
 | 
			
		||||
 | 
			
		||||
            $model['advanced']['force_scales'] = [
 | 
			
		||||
| 
						 | 
				
			
			@ -174,32 +194,32 @@ class studyplan {
 | 
			
		|||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function add($fields,$bare=false){
 | 
			
		||||
    public static function add($fields, $bare=false) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $addable = ['name','shortname','description','idnumber','context_id','aggregation','aggregation_config'];
 | 
			
		||||
        $addable = ['name', 'shortname', 'description', 'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
 | 
			
		||||
        $info = ['enddate' => null ];
 | 
			
		||||
        foreach($addable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        foreach ($addable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $id = $DB->insert_record(self::TABLE, $info);
 | 
			
		||||
        $plan = self::findById($id); // make sure the new studyplan is immediately cached
 | 
			
		||||
        $plan = self::findById($id); // make sure the new studyplan is immediately cached.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Start temporary skräpp code
 | 
			
		||||
        // Add a single page and copy the names.This keeps the data sane until the upgrade to 
 | 
			
		||||
        // real page management is done
 | 
			
		||||
        // Start temporary skräpp code.
 | 
			
		||||
        // Add a single page and copy the names.This keeps the data sane until the upgrade to .
 | 
			
		||||
        // real page management is done.
 | 
			
		||||
        // On import, adding an empty page messes things up for now, so we have an option to skip this....
 | 
			
		||||
        // TODO: Remove this when proper page management is implemented
 | 
			
		||||
        if(!$bare){
 | 
			
		||||
        // TODO: Remove this when proper page management is implemented.
 | 
			
		||||
        if (!$bare) {
 | 
			
		||||
 | 
			
		||||
            $pageaddable = ['name','shortname','description','periods','startdate','enddate'];
 | 
			
		||||
            $pageaddable = ['name', 'shortname', 'description', 'periods', 'startdate', 'enddate'];
 | 
			
		||||
            $pageinfo = ['studyplan_id' => $id];
 | 
			
		||||
            foreach($pageaddable as $f){
 | 
			
		||||
                if(array_key_exists($f,$fields)){
 | 
			
		||||
                    if($f == "name"){
 | 
			
		||||
            foreach ($pageaddable as $f) {
 | 
			
		||||
                if (array_key_exists($f, $fields)) {
 | 
			
		||||
                    if ($f == "name") {
 | 
			
		||||
                        $pageinfo["fullname"] = $fields[$f];
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $pageinfo[$f] = $fields[$f];
 | 
			
		||||
| 
						 | 
				
			
			@ -210,41 +230,41 @@ class studyplan {
 | 
			
		|||
            $page = studyplanpage::add($pageinfo);
 | 
			
		||||
            $plan->page_cache = [$page];
 | 
			
		||||
        }
 | 
			
		||||
        // End temporary skräpp code
 | 
			
		||||
        // End temporary skräpp code.
 | 
			
		||||
 | 
			
		||||
        return $plan;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function edit($fields){
 | 
			
		||||
    public function edit($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $editable = ['name','shortname','description','idnumber','context_id','aggregation','aggregation_config'];
 | 
			
		||||
        $info = ['id' => $this->id,];
 | 
			
		||||
        foreach($editable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        $editable = ['name', 'shortname', 'description', 'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
 | 
			
		||||
        $info = ['id' => $this->id, ];
 | 
			
		||||
        foreach ($editable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $DB->update_record(self::TABLE, $info);
 | 
			
		||||
        //reload record after edit
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $this->id],"*",MUST_EXIST);
 | 
			
		||||
        //reload record after edit.
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST);
 | 
			
		||||
 | 
			
		||||
        //reload the context...
 | 
			
		||||
        $this->context = null;
 | 
			
		||||
        $this->context();
 | 
			
		||||
        // reload aggregator
 | 
			
		||||
        // reload aggregator.
 | 
			
		||||
        $this->aggregator = aggregator::createOrDefault($this->r->aggregation, $this->r->aggregation_config);
 | 
			
		||||
 | 
			
		||||
        // Start temporary skräpp code
 | 
			
		||||
        // TODO: Until proper page editing is implemented, copy data from studyplan to it's first page
 | 
			
		||||
        // Start temporary skräpp code.
 | 
			
		||||
        // TODO: Until proper page editing is implemented, copy data from studyplan to it's first page.
 | 
			
		||||
        // This keeps the data sane until the upgrade is done.
 | 
			
		||||
        if(count($this->pages()) == 1){
 | 
			
		||||
            // update the info to the page as well
 | 
			
		||||
        if (count($this->pages()) == 1) {
 | 
			
		||||
            // update the info to the page as well.
 | 
			
		||||
            $page = $this->pages()[0];
 | 
			
		||||
            $pageeditable = ['name','shortname','description','periods','startdate','enddate'];
 | 
			
		||||
            $pageeditable = ['name', 'shortname', 'description', 'periods', 'startdate', 'enddate'];
 | 
			
		||||
            $pageinfo = [];
 | 
			
		||||
            foreach($pageeditable as $f){
 | 
			
		||||
                if(array_key_exists($f,$fields)){
 | 
			
		||||
                    if($f == "name"){
 | 
			
		||||
            foreach ($pageeditable as $f) {
 | 
			
		||||
                if (array_key_exists($f, $fields)) {
 | 
			
		||||
                    if ($f == "name") {
 | 
			
		||||
                        $pageinfo["fullname"] = $fields[$f];
 | 
			
		||||
                    }else {
 | 
			
		||||
                        $pageinfo[$f] = $fields[$f];
 | 
			
		||||
| 
						 | 
				
			
			@ -253,22 +273,22 @@ class studyplan {
 | 
			
		|||
            }
 | 
			
		||||
            $page->edit($pageinfo);
 | 
			
		||||
        }
 | 
			
		||||
        // End temporary skräpp code
 | 
			
		||||
        // End temporary skräpp code.
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($force=false){
 | 
			
		||||
    public function delete($force=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if($force){
 | 
			
		||||
        if ($force) {
 | 
			
		||||
            $children = studyplanpage::find_studyplan_children($this);
 | 
			
		||||
            foreach($children as $c){
 | 
			
		||||
            foreach ($children as $c) {
 | 
			
		||||
                $c->delete($force);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($DB->count_records('local_treestudyplan_page',['studyplan_id' => $this->id]) > 0){
 | 
			
		||||
        if ($DB->count_records('local_treestudyplan_page', ['studyplan_id' => $this->id]) > 0) {
 | 
			
		||||
            return success::fail('cannot delete studyplan that still has pages');
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
| 
						 | 
				
			
			@ -278,25 +298,24 @@ class studyplan {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function find_all($contextid=-1){
 | 
			
		||||
    public static function find_all($contextid=-1) {
 | 
			
		||||
        global $DB, $USER;
 | 
			
		||||
        $list = [];
 | 
			
		||||
 | 
			
		||||
        if($contextid <= 0){
 | 
			
		||||
            $ids = $DB->get_fieldset_select(self::TABLE,"id","");
 | 
			
		||||
        if ($contextid <= 0) {
 | 
			
		||||
            $ids = $DB->get_fieldset_select(self::TABLE, "id", "");
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            if($contextid == 1){
 | 
			
		||||
            if ($contextid == 1) {
 | 
			
		||||
                $contextid = 1;
 | 
			
		||||
                $where = "context_id <= :contextid OR context_id IS NULL";
 | 
			
		||||
            } else {
 | 
			
		||||
                $where = "context_id = :contextid";
 | 
			
		||||
            }
 | 
			
		||||
            $ids = $DB->get_fieldset_select(self::TABLE,"id",$where,["contextid" => $contextid]);
 | 
			
		||||
            $ids = $DB->get_fieldset_select(self::TABLE, "id", $where, ["contextid" => $contextid]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach($ids as $id)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($ids as $id) {
 | 
			
		||||
            $list[] = studyplan::findById($id);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
| 
						 | 
				
			
			@ -307,19 +326,17 @@ class studyplan {
 | 
			
		|||
        $list = [];
 | 
			
		||||
 | 
			
		||||
        $where = "shortname = :shortname AND context_id = :contextid";
 | 
			
		||||
        if($contextid == 0){
 | 
			
		||||
        if ($contextid == 0) {
 | 
			
		||||
            $where .= "OR context_id IS NULL";
 | 
			
		||||
        }
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE,"id",$where,["shortname"=>$shortname, "contextid" => $contextid]);
 | 
			
		||||
        foreach($ids as $id)
 | 
			
		||||
        {
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE, "id", $where, ["shortname"=>$shortname, "contextid" => $contextid]);
 | 
			
		||||
        foreach ($ids as $id) {
 | 
			
		||||
            $list[] = studyplan::findById($id);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function find_for_user($userid)
 | 
			
		||||
    {
 | 
			
		||||
    public static function find_for_user($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT s.id FROM {local_treestudyplan} s
 | 
			
		||||
| 
						 | 
				
			
			@ -334,11 +351,11 @@ class studyplan {
 | 
			
		|||
        $user_plan_ids = $DB->get_fieldset_sql($sql, ['userid' => $userid]);
 | 
			
		||||
 | 
			
		||||
        $plans = [];
 | 
			
		||||
        foreach($cohort_plan_ids as $id) {
 | 
			
		||||
        foreach ($cohort_plan_ids as $id) {
 | 
			
		||||
            $plans[$id] = self::findById($id);
 | 
			
		||||
        }
 | 
			
		||||
        foreach($user_plan_ids as $id) {
 | 
			
		||||
            if(!array_key_exists($id,$plans)){
 | 
			
		||||
        foreach ($user_plan_ids as $id) {
 | 
			
		||||
            if (!array_key_exists($id, $plans)) {
 | 
			
		||||
                $plans[$id] = self::findById($id);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -347,8 +364,7 @@ class studyplan {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static public function exist_for_user($userid)
 | 
			
		||||
    {
 | 
			
		||||
    static public function exist_for_user($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $count = 0;
 | 
			
		||||
        $sql = "SELECT s.* FROM {local_treestudyplan} s
 | 
			
		||||
| 
						 | 
				
			
			@ -369,15 +385,15 @@ class studyplan {
 | 
			
		|||
     * Retrieve the users linked to this studyplan.
 | 
			
		||||
     * @return array of User objects
 | 
			
		||||
     */
 | 
			
		||||
    public function find_linked_users(){
 | 
			
		||||
    public function find_linked_users() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $users = [];
 | 
			
		||||
 | 
			
		||||
        $uids = $this->find_linked_userids();
 | 
			
		||||
 | 
			
		||||
        foreach($uids as $uid){
 | 
			
		||||
            $users[] = $DB->get_record("user",["id"=>$uid]);
 | 
			
		||||
        foreach ($uids as $uid) {
 | 
			
		||||
            $users[] = $DB->get_record("user", ["id"=>$uid]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $users;
 | 
			
		||||
| 
						 | 
				
			
			@ -390,25 +406,25 @@ class studyplan {
 | 
			
		|||
    public function find_linked_userids(): array {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if($this->linked_userids === null){
 | 
			
		||||
        if ($this->linked_userids === null) {
 | 
			
		||||
            $uids = [];
 | 
			
		||||
            // First get directly linked userids
 | 
			
		||||
            // First get directly linked userids.
 | 
			
		||||
            $sql = "SELECT j.user_id FROM {local_treestudyplan_user} j
 | 
			
		||||
                    WHERE j.studyplan_id  = :planid";
 | 
			
		||||
            $ulist = $DB->get_fieldset_sql($sql, ['planid' => $this->id]);
 | 
			
		||||
 | 
			
		||||
            $uids = array_merge($uids,$ulist);
 | 
			
		||||
            foreach($ulist as $uid){
 | 
			
		||||
                $users[] = $DB->get_record("user",["id"=>$uid]);
 | 
			
		||||
            $uids = array_merge($uids, $ulist);
 | 
			
		||||
            foreach ($ulist as $uid) {
 | 
			
		||||
                $users[] = $DB->get_record("user", ["id"=>$uid]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Next het users linked though cohort
 | 
			
		||||
            // Next het users linked though cohort.
 | 
			
		||||
            $sql = "SELECT cm.userid FROM  {local_treestudyplan_cohort} j
 | 
			
		||||
                    INNER JOIN {cohort_members} cm ON j.cohort_id = cm.cohortid
 | 
			
		||||
                    WHERE j.studyplan_id = :planid";
 | 
			
		||||
            $ulist = $DB->get_fieldset_sql($sql, ['planid' => $this->id]);
 | 
			
		||||
 | 
			
		||||
            $uids = array_merge($uids,$ulist);
 | 
			
		||||
            $uids = array_merge($uids, $ulist);
 | 
			
		||||
 | 
			
		||||
            $this->linked_userids = array_unique($uids);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -418,21 +434,21 @@ class studyplan {
 | 
			
		|||
    /** Check if this studyplan is linked to a particular user
 | 
			
		||||
     * @param bool|stdClass $user The userid or user record of the user
 | 
			
		||||
     */
 | 
			
		||||
    public function has_linked_user($user){
 | 
			
		||||
        if(is_int($user)){
 | 
			
		||||
    public function has_linked_user($user) {
 | 
			
		||||
        if (is_int($user)) {
 | 
			
		||||
            $userid = $user;
 | 
			
		||||
        } else {
 | 
			
		||||
            $userid = $user->id;
 | 
			
		||||
        }
 | 
			
		||||
        $uids = $this->find_linked_userids();
 | 
			
		||||
        if(in_array($userid,$uids)){
 | 
			
		||||
        if (in_array($userid, $uids)) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyplan'),
 | 
			
		||||
            "name" => new \external_value(PARAM_TEXT, 'name of studyplan'),
 | 
			
		||||
| 
						 | 
				
			
			@ -441,11 +457,11 @@ class studyplan {
 | 
			
		|||
            "idnumber"=> new \external_value(PARAM_TEXT, 'idnumber of curriculum'),
 | 
			
		||||
            "pages" => new \external_multiple_structure(studyplanpage::user_structure()),
 | 
			
		||||
            "aggregation_info" => aggregator::basic_structure(),
 | 
			
		||||
        ],'Studyplan with user info',$value);
 | 
			
		||||
        ], 'Studyplan with user info', $value);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid){
 | 
			
		||||
    public function user_model($userid) {
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
            'id' => $this->r->id,
 | 
			
		||||
| 
						 | 
				
			
			@ -457,59 +473,53 @@ class studyplan {
 | 
			
		|||
            'aggregation_info' => $this->aggregator->basic_model(),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        foreach($this->pages() as $p)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($this->pages() as $p) {
 | 
			
		||||
            $model['pages'][] = $p->user_model($userid);
 | 
			
		||||
        }
 | 
			
		||||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function duplicate_plan($plan_id,$name,$shortname)
 | 
			
		||||
    {
 | 
			
		||||
    public static function duplicate_plan($plan_id, $name, $shortname) {
 | 
			
		||||
        $ori = self::findById($plan_id);
 | 
			
		||||
        $new = $ori->duplicate($name,$shortname);
 | 
			
		||||
        $new = $ori->duplicate($name, $shortname);
 | 
			
		||||
        return $new->simple_model();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function duplicate($name,$shortname)
 | 
			
		||||
    {
 | 
			
		||||
        // First duplicate the studyplan structure
 | 
			
		||||
    public function duplicate($name, $shortname) {
 | 
			
		||||
        // First duplicate the studyplan structure.
 | 
			
		||||
        $newplan =studyplan::add([
 | 
			
		||||
            'name' => $name,
 | 
			
		||||
            'shortname' => $shortname,
 | 
			
		||||
            'description' => $this->r->description,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // next, copy the studylines
 | 
			
		||||
        // next, copy the studylines.
 | 
			
		||||
 | 
			
		||||
        foreach($this->pages() as $p){
 | 
			
		||||
        foreach ($this->pages() as $p) {
 | 
			
		||||
            $newchild = $p->duplicate($newplan);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $newplan;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function export_structure()
 | 
			
		||||
    {
 | 
			
		||||
    public static function export_structure() {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "format" => new \external_value(PARAM_TEXT, 'format of studyplan export'),
 | 
			
		||||
            "content"=> new \external_value(PARAM_TEXT, 'exported studyplan content'),
 | 
			
		||||
        ],'Exported studyplan');
 | 
			
		||||
        ], 'Exported studyplan');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_plan()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_plan() {
 | 
			
		||||
        $model = $this->export_model();
 | 
			
		||||
        $json = json_encode([
 | 
			
		||||
            "type"=>"studyplan",
 | 
			
		||||
            "version"=>2.0,
 | 
			
		||||
            "studyplan"=>$model
 | 
			
		||||
            ],\JSON_PRETTY_PRINT);
 | 
			
		||||
            ], \JSON_PRETTY_PRINT);
 | 
			
		||||
        return [ "format" => "application/json", "content" => $json];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_model()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_model() {
 | 
			
		||||
        $model = [
 | 
			
		||||
            'name' => $this->r->name,
 | 
			
		||||
            'shortname' => $this->r->shortname,
 | 
			
		||||
| 
						 | 
				
			
			@ -522,32 +532,29 @@ class studyplan {
 | 
			
		|||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_pages_model()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_pages_model() {
 | 
			
		||||
        $pages = [];
 | 
			
		||||
        foreach($this->pages() as $p)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($this->pages() as $p) {
 | 
			
		||||
            $pages[] = $p->export_model();
 | 
			
		||||
        }
 | 
			
		||||
        return $pages;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function import_studyplan($content,$format="application/json",$context_id=1)
 | 
			
		||||
    {
 | 
			
		||||
        if($format != "application/json") { return false;}
 | 
			
		||||
    public static function import_studyplan($content, $format="application/json", $context_id=1) {
 | 
			
		||||
        if ($format != "application/json") { return false;}
 | 
			
		||||
 | 
			
		||||
        $content = json_decode($content,true);
 | 
			
		||||
        if($content["type"] == "studyplan" && $content["version"] >= 2.0){
 | 
			
		||||
        $content = json_decode($content, true);
 | 
			
		||||
        if ($content["type"] == "studyplan" && $content["version"] >= 2.0) {
 | 
			
		||||
 | 
			
		||||
            // Make sure the aggregation_config is re-encoded as json text
 | 
			
		||||
            // Make sure the aggregation_config is re-encoded as json text.
 | 
			
		||||
            $content["studyplan"]["aggregation_config"] = json_encode($content["studyplan"]["aggregation_config"]);
 | 
			
		||||
 | 
			
		||||
            // And make sure the context_id is set to the provided context for import
 | 
			
		||||
            // And make sure the context_id is set to the provided context for import.
 | 
			
		||||
            $content["studyplan"]["context_id"] = $context_id;
 | 
			
		||||
 | 
			
		||||
            // Create a new plan, based on the given parameters - this is the import studyplan part
 | 
			
		||||
            $plan = self::add($content["studyplan"],true);
 | 
			
		||||
            // Now import each page
 | 
			
		||||
            // Create a new plan, based on the given parameters - this is the import studyplan part.
 | 
			
		||||
            $plan = self::add($content["studyplan"], true);
 | 
			
		||||
            // Now import each page.
 | 
			
		||||
            return $plan->import_pages_model($content["studyplan"]["pages"]);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -557,17 +564,16 @@ class studyplan {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function import_pages($content,$format="application/json")
 | 
			
		||||
    {
 | 
			
		||||
        if($format != "application/json") { return false;}
 | 
			
		||||
        $content = json_decode($content,true);
 | 
			
		||||
        if($content["version"] >= 2.0){
 | 
			
		||||
            if($content["type"] == "studyplanpage"){
 | 
			
		||||
                // import single page from a studyplanpage (wrapped in array of one page)
 | 
			
		||||
    public function import_pages($content, $format="application/json") {
 | 
			
		||||
        if ($format != "application/json") { return false;}
 | 
			
		||||
        $content = json_decode($content, true);
 | 
			
		||||
        if ($content["version"] >= 2.0) {
 | 
			
		||||
            if ($content["type"] == "studyplanpage") {
 | 
			
		||||
                // import single page from a studyplanpage (wrapped in array of one page).
 | 
			
		||||
                return $this->import_pages_model([$content["page"]]);
 | 
			
		||||
            }
 | 
			
		||||
            else if($content["type"] == "studyplan"){
 | 
			
		||||
                // Import all pages from the studyplan
 | 
			
		||||
            else if ($content["type"] == "studyplan") {
 | 
			
		||||
                // Import all pages from the studyplan.
 | 
			
		||||
                return $this->import_pages_model($content["studyplan"]["pages"]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -576,10 +582,9 @@ class studyplan {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function import_pages_model($model)
 | 
			
		||||
    {
 | 
			
		||||
    protected function import_pages_model($model) {
 | 
			
		||||
        $this->pages(); // make sure the page cache is initialized, since we will be adding to it.
 | 
			
		||||
        foreach($model as $p){
 | 
			
		||||
        foreach ($model as $p) {
 | 
			
		||||
            $p["studyplan_id"] = $this->id();
 | 
			
		||||
            $page = studyplanpage::add($p);
 | 
			
		||||
            $this->page_cache[] = $page;
 | 
			
		||||
| 
						 | 
				
			
			@ -592,29 +597,29 @@ class studyplan {
 | 
			
		|||
    /**
 | 
			
		||||
     * Mark the studyplan as changed regarding courses and associated cohorts
 | 
			
		||||
     */
 | 
			
		||||
    public function mark_csync_changed(){
 | 
			
		||||
    public function mark_csync_changed() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $DB->update_record(self::TABLE, ['id' => $this->id,"csync_flag" => 1]);
 | 
			
		||||
        $this->r->csync_flag = 1; //manually set it in the cache, if something unexpected happened, an exception has already been thrown anyway
 | 
			
		||||
        $DB->update_record(self::TABLE, ['id' => $this->id, "csync_flag" => 1]);
 | 
			
		||||
        $this->r->csync_flag = 1; //manually set it in the cache, if something unexpected happened, an exception has already been thrown anyway.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Clear the csync mark
 | 
			
		||||
     */
 | 
			
		||||
    public function clear_csync_changed(){
 | 
			
		||||
    public function clear_csync_changed() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $DB->update_record(self::TABLE, ['id' => $this->id,"csync_flag" => 0]);
 | 
			
		||||
        $this->r->csync_flag = 0; //manually set it in the cache, if something unexpected happened, an exception has already been thrown anyway
 | 
			
		||||
        $DB->update_record(self::TABLE, ['id' => $this->id, "csync_flag" => 0]);
 | 
			
		||||
        $this->r->csync_flag = 0; //manually set it in the cache, if something unexpected happened, an exception has already been thrown anyway.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function has_csync_changed(){
 | 
			
		||||
    public function has_csync_changed() {
 | 
			
		||||
        return ($this->r->csync_flag > 0)?true:false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * See if the specified course id is linked in this studyplan
 | 
			
		||||
     */
 | 
			
		||||
    public function course_linked($courseid){
 | 
			
		||||
    public function course_linked($courseid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT COUNT(i.id)
 | 
			
		||||
| 
						 | 
				
			
			@ -623,7 +628,7 @@ class studyplan {
 | 
			
		|||
                INNER JOIN {local_treestudyplan_item} i ON l.id = i.line_id
 | 
			
		||||
                WHERE p.id = :planid
 | 
			
		||||
                   AND i.course_id = :courseid";
 | 
			
		||||
        $count = $DB->get_field_sql($sql,["courseid" => $courseid, "planid" => $this->id]);
 | 
			
		||||
        $count = $DB->get_field_sql($sql, ["courseid" => $courseid, "planid" => $this->id]);
 | 
			
		||||
 | 
			
		||||
        return ($count > 0)?true:false;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -632,7 +637,7 @@ class studyplan {
 | 
			
		|||
     * List the course id is linked in this studyplan
 | 
			
		||||
     * Used for cohort enrolment cascading
 | 
			
		||||
     */
 | 
			
		||||
    public function get_linked_course_ids(){
 | 
			
		||||
    public function get_linked_course_ids() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT i.course_id
 | 
			
		||||
| 
						 | 
				
			
			@ -641,7 +646,7 @@ class studyplan {
 | 
			
		|||
                INNER JOIN {local_treestudyplan_line} l ON pg.id = l.page_id
 | 
			
		||||
                INNER JOIN {local_treestudyplan_item} i ON l.id = i.line_id
 | 
			
		||||
                WHERE p.id = :studyplan_id AND i.type = :itemtype";
 | 
			
		||||
        $fields = $DB->get_fieldset_sql($sql,["studyplan_id" => $this->id,"itemtype" => studyitem::COURSE]);
 | 
			
		||||
        $fields = $DB->get_fieldset_sql($sql, ["studyplan_id" => $this->id, "itemtype" => studyitem::COURSE]);
 | 
			
		||||
 | 
			
		||||
        return $fields;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -649,7 +654,7 @@ class studyplan {
 | 
			
		|||
    /**
 | 
			
		||||
     * List the cohort id's associated with this studyplan
 | 
			
		||||
     */
 | 
			
		||||
    public function get_linked_cohort_ids(){
 | 
			
		||||
    public function get_linked_cohort_ids() {
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT DISTINCT j.cohort_id FROM {local_treestudyplan_cohort} j
 | 
			
		||||
| 
						 | 
				
			
			@ -661,7 +666,7 @@ class studyplan {
 | 
			
		|||
    /**
 | 
			
		||||
     * List the user id's explicitly associated with this studyplan
 | 
			
		||||
     */
 | 
			
		||||
    public function get_linked_user_ids(){
 | 
			
		||||
    public function get_linked_user_ids() {
 | 
			
		||||
		global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT DISTINCT j.user_id FROM {local_treestudyplan_user} j
 | 
			
		||||
| 
						 | 
				
			
			@ -672,7 +677,7 @@ class studyplan {
 | 
			
		|||
    /**
 | 
			
		||||
     * See if the specified course id is linked in this studyplan
 | 
			
		||||
     */
 | 
			
		||||
    public function badge_linked($badgeid){
 | 
			
		||||
    public function badge_linked($badgeid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT COUNT(i.id)
 | 
			
		||||
| 
						 | 
				
			
			@ -681,7 +686,7 @@ class studyplan {
 | 
			
		|||
                INNER JOIN {local_treestudyplan_item} i ON l.id = i.line_id
 | 
			
		||||
                WHERE p.id = :planid
 | 
			
		||||
                   AND i.badge_id = :badgeid";
 | 
			
		||||
        $count = $DB->get_field_sql($sql,["badgeid" => $badgeid, "planid" => $this->id]);
 | 
			
		||||
        $count = $DB->get_field_sql($sql, ["badgeid" => $badgeid, "planid" => $this->id]);
 | 
			
		||||
 | 
			
		||||
        return ($count > 0)?true:false;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/externallib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -10,17 +31,17 @@ class studyplanpage {
 | 
			
		|||
    private static $CACHE = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private $r; // Holds database record
 | 
			
		||||
    private $r; // Holds database record.
 | 
			
		||||
    private $id;
 | 
			
		||||
    private $studyplan;
 | 
			
		||||
 | 
			
		||||
    public function aggregator(){ 
 | 
			
		||||
    public function aggregator() {
 | 
			
		||||
        return $this->studyplan->aggregator();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Cache constructors to avoid multiple creation events in one session.
 | 
			
		||||
    public static function findById($id): self {
 | 
			
		||||
        if(!array_key_exists($id,self::$CACHE)){
 | 
			
		||||
        if (!array_key_exists($id, self::$CACHE)) {
 | 
			
		||||
            self::$CACHE[$id] = new self($id);
 | 
			
		||||
        }
 | 
			
		||||
        return self::$CACHE[$id];
 | 
			
		||||
| 
						 | 
				
			
			@ -29,11 +50,11 @@ class studyplanpage {
 | 
			
		|||
    private function __construct($id) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $this->id = $id;
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $id]);
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $id]);
 | 
			
		||||
        $this->studyplan = studyplan::findById($this->r->studyplan_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function id(){
 | 
			
		||||
    public function id() {
 | 
			
		||||
        return $this->id;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,34 +62,34 @@ class studyplanpage {
 | 
			
		|||
        return $this->studyplan;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function shortname(){
 | 
			
		||||
    public function shortname() {
 | 
			
		||||
        return $this->r->shortname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function periods(){
 | 
			
		||||
    public function periods() {
 | 
			
		||||
        return $this->r->periods;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function fullname(){
 | 
			
		||||
    public function fullname() {
 | 
			
		||||
        return $this->r->fullname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function startdate(){
 | 
			
		||||
    public function startdate() {
 | 
			
		||||
        return new \DateTime($this->r->startdate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function enddate(){
 | 
			
		||||
        if($this->r->enddate && strlen($this->r->enddate) > 0){
 | 
			
		||||
    public function enddate() {
 | 
			
		||||
        if ($this->r->enddate && strlen($this->r->enddate) > 0) {
 | 
			
		||||
            return new \DateTime($this->r->enddate);
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            // return a date 100 years into the future
 | 
			
		||||
            // return a date 100 years into the future.
 | 
			
		||||
            return (new \DateTime($this->r->startdate))->add(new \DateInterval("P100Y"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function simple_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function simple_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyplan page'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'name of studyplan page'),
 | 
			
		||||
| 
						 | 
				
			
			@ -78,10 +99,10 @@ class studyplanpage {
 | 
			
		|||
            "startdate" => new \external_value(PARAM_TEXT, 'start date of studyplan'),
 | 
			
		||||
            "enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan'),
 | 
			
		||||
            "perioddesc" => period::page_structure(),
 | 
			
		||||
        ],'Studyplan page basic info',$value);
 | 
			
		||||
        ], 'Studyplan page basic info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function simple_model(){
 | 
			
		||||
    public function simple_model() {
 | 
			
		||||
        return [
 | 
			
		||||
            'id' => $this->r->id,
 | 
			
		||||
            'fullname' => $this->r->fullname,
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +115,7 @@ class studyplanpage {
 | 
			
		|||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function editor_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyplan'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'name of studyplan page'),
 | 
			
		||||
| 
						 | 
				
			
			@ -105,10 +126,10 @@ class studyplanpage {
 | 
			
		|||
            "enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan page'),
 | 
			
		||||
            "studylines" => new \external_multiple_structure(studyline::editor_structure()),
 | 
			
		||||
            "perioddesc" => period::page_structure(),
 | 
			
		||||
            ],'Studyplan page full structure',$value);
 | 
			
		||||
            ], 'Studyplan page full structure', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function editor_model(){
 | 
			
		||||
    public function editor_model() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
| 
						 | 
				
			
			@ -124,68 +145,67 @@ class studyplanpage {
 | 
			
		|||
        ];
 | 
			
		||||
 | 
			
		||||
        $children = studyline::find_page_children($this);
 | 
			
		||||
        foreach($children as $c)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            $model['studylines'][] = $c->editor_model();
 | 
			
		||||
        }
 | 
			
		||||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function add($fields){
 | 
			
		||||
    public static function add($fields) {
 | 
			
		||||
        global $CFG, $DB;
 | 
			
		||||
 | 
			
		||||
        if(!isset($fields['studyplan_id'])){
 | 
			
		||||
        if (!isset($fields['studyplan_id'])) {
 | 
			
		||||
            throw new \InvalidArgumentException("parameter 'studyplan_id' missing");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $addable = ['studyplan_id','fullname','shortname','description','periods','startdate','enddate'];
 | 
			
		||||
        $addable = ['studyplan_id', 'fullname', 'shortname', 'description', 'periods', 'startdate', 'enddate'];
 | 
			
		||||
        $info = ['enddate' => null ];
 | 
			
		||||
        foreach($addable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        foreach ($addable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if(!isset($addable['periods'])){
 | 
			
		||||
        if (!isset($addable['periods'])) {
 | 
			
		||||
            $addable['periods'] = 4;
 | 
			
		||||
        } else if($addable['periods'] < 1){
 | 
			
		||||
        } else if ($addable['periods'] < 1) {
 | 
			
		||||
            $addable['periods'] = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $id = $DB->insert_record(self::TABLE, $info);
 | 
			
		||||
        return self::findById($id); // make sure the new page is immediately cached
 | 
			
		||||
        return self::findById($id); // make sure the new page is immediately cached.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function edit($fields){
 | 
			
		||||
    public function edit($fields) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $editable = ['fullname','shortname','description','periods','startdate','enddate'];
 | 
			
		||||
        $info = ['id' => $this->id,];
 | 
			
		||||
        foreach($editable as $f){
 | 
			
		||||
            if(array_key_exists($f,$fields)){
 | 
			
		||||
        $editable = ['fullname', 'shortname', 'description', 'periods', 'startdate', 'enddate'];
 | 
			
		||||
        $info = ['id' => $this->id, ];
 | 
			
		||||
        foreach ($editable as $f) {
 | 
			
		||||
            if (array_key_exists($f, $fields)) {
 | 
			
		||||
                $info[$f] = $fields[$f];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if(isset($info['periods']) && $info['periods'] < 1){
 | 
			
		||||
        if (isset($info['periods']) && $info['periods'] < 1) {
 | 
			
		||||
            $info['periods'] = 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $DB->update_record(self::TABLE, $info);
 | 
			
		||||
        //reload record after edit
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE,['id' => $this->id],"*",MUST_EXIST);
 | 
			
		||||
        //reload record after edit.
 | 
			
		||||
        $this->r = $DB->get_record(self::TABLE, ['id' => $this->id], "*", MUST_EXIST);
 | 
			
		||||
 | 
			
		||||
        return $this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function delete($force=false){
 | 
			
		||||
    public function delete($force=false) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
 | 
			
		||||
        if($force){
 | 
			
		||||
        if ($force) {
 | 
			
		||||
            $children = studyline::find_page_children($this);
 | 
			
		||||
            foreach($children as $c){
 | 
			
		||||
            foreach ($children as $c) {
 | 
			
		||||
                $c->delete($force);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if($DB->count_records('local_treestudyplan_line',['page_id' => $this->id]) > 0){
 | 
			
		||||
        if ($DB->count_records('local_treestudyplan_line', ['page_id' => $this->id]) > 0) {
 | 
			
		||||
            return success::fail('cannot delete studyplan page that still has studylines');
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +215,7 @@ class studyplanpage {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED){
 | 
			
		||||
    public static function user_structure($value=VALUE_REQUIRED) {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "id" => new \external_value(PARAM_INT, 'id of studyplan page'),
 | 
			
		||||
            "fullname" => new \external_value(PARAM_TEXT, 'name of studyplan page'),
 | 
			
		||||
| 
						 | 
				
			
			@ -206,10 +226,10 @@ class studyplanpage {
 | 
			
		|||
            "enddate" => new \external_value(PARAM_TEXT, 'end date of studyplan page'),
 | 
			
		||||
            "studylines" => new \external_multiple_structure(studyline::user_structure()),
 | 
			
		||||
            "perioddesc" => period::page_structure(),
 | 
			
		||||
        ],'Studyplan page with user info',$value);
 | 
			
		||||
        ], 'Studyplan page with user info', $value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function user_model($userid){
 | 
			
		||||
    public function user_model($userid) {
 | 
			
		||||
 | 
			
		||||
        $model = [
 | 
			
		||||
            'id' => $this->r->id,
 | 
			
		||||
| 
						 | 
				
			
			@ -224,35 +244,31 @@ class studyplanpage {
 | 
			
		|||
        ];
 | 
			
		||||
 | 
			
		||||
        $children = studyline::find_page_children($this);
 | 
			
		||||
        foreach($children as $c)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            $model['studylines'][] = $c->user_model($userid);
 | 
			
		||||
        }
 | 
			
		||||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function find_studyplan_children(studyplan $plan)
 | 
			
		||||
    {
 | 
			
		||||
    public static function find_studyplan_children(studyplan $plan) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $list = [];
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE,"id","studyplan_id = :plan_id ORDER BY startdate",
 | 
			
		||||
        $ids = $DB->get_fieldset_select(self::TABLE, "id", "studyplan_id = :plan_id ORDER BY startdate",
 | 
			
		||||
                                        ['plan_id' => $plan->id()]);
 | 
			
		||||
        foreach($ids as $id) {
 | 
			
		||||
        foreach ($ids as $id) {
 | 
			
		||||
            $list[] = self::findById($id);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function duplicate_page($page_id,$name,$shortname)
 | 
			
		||||
    {
 | 
			
		||||
    public static function duplicate_page($page_id, $name, $shortname) {
 | 
			
		||||
        $ori = self::findById($page_id);
 | 
			
		||||
        $new = $ori->duplicate($name,$shortname);
 | 
			
		||||
        $new = $ori->duplicate($name, $shortname);
 | 
			
		||||
        return $new->simple_model();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function duplicate($new_studyplan)
 | 
			
		||||
    {
 | 
			
		||||
        // First duplicate the studyplan structure
 | 
			
		||||
    public function duplicate($new_studyplan) {
 | 
			
		||||
        // First duplicate the studyplan structure.
 | 
			
		||||
        $new = studyplanpage::add([
 | 
			
		||||
            'studyplan_id' => $new_studyplan->id(),
 | 
			
		||||
            'fullname' => $this->r->fullname,
 | 
			
		||||
| 
						 | 
				
			
			@ -263,97 +279,94 @@ class studyplanpage {
 | 
			
		|||
            'enddate' => empty($this->r->enddate)?null:$this->r->enddate,
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        // next, copy the studylines
 | 
			
		||||
        // next, copy the studylines.
 | 
			
		||||
 | 
			
		||||
        $children = studyline::find_page_children($this);
 | 
			
		||||
        $itemtranslation = [];
 | 
			
		||||
        $linetranslation = [];
 | 
			
		||||
        foreach($children as $c){
 | 
			
		||||
            $newchild = $c->duplicate($this,$itemtranslation);
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            $newchild = $c->duplicate($this, $itemtranslation);
 | 
			
		||||
            $linetranslation[$c->id()] = $newchild->id();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // now the itemtranslation array contains all of the old child id's as keys and all of the related new ids as values
 | 
			
		||||
        // (feature of the studyline::duplicate function)
 | 
			
		||||
        // use this to recreate the lines in the new plan
 | 
			
		||||
        foreach(array_keys($itemtranslation) as $item_id){
 | 
			
		||||
            // copy based on the outgoing connections of each item, to avoid duplicates
 | 
			
		||||
        // now the itemtranslation array contains all of the old child id's as keys and all of the related new ids as values.
 | 
			
		||||
        // (feature of the studyline::duplicate function).
 | 
			
		||||
        // use this to recreate the lines in the new plan.
 | 
			
		||||
        foreach (array_keys($itemtranslation) as $item_id) {
 | 
			
		||||
            // copy based on the outgoing connections of each item, to avoid duplicates.
 | 
			
		||||
            $connections  = studyitemconnection::find_outgoing($item_id);
 | 
			
		||||
            foreach($connections as $conn){
 | 
			
		||||
                studyitemconnection::connect($itemtranslation[$conn->from_id],$itemtranslation[$conn->to_id]);
 | 
			
		||||
            foreach ($connections as $conn) {
 | 
			
		||||
                studyitemconnection::connect($itemtranslation[$conn->from_id], $itemtranslation[$conn->to_id]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $new;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function export_structure()
 | 
			
		||||
    {
 | 
			
		||||
    public static function export_structure() {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "format" => new \external_value(PARAM_TEXT, 'format of studyplan export'),
 | 
			
		||||
            "content"=> new \external_value(PARAM_TEXT, 'exported studyplan content'),
 | 
			
		||||
        ],'Exported studyplan');
 | 
			
		||||
        ], 'Exported studyplan');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_page()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_page() {
 | 
			
		||||
        $model = $this->export_model();
 | 
			
		||||
        $json = json_encode([
 | 
			
		||||
            "type"=>"studyplanpage",
 | 
			
		||||
            "version"=>2.0,
 | 
			
		||||
            "page"=>$model
 | 
			
		||||
            ],\JSON_PRETTY_PRINT);
 | 
			
		||||
            ], \JSON_PRETTY_PRINT);
 | 
			
		||||
        return [ "format" => "application/json", "content" => $json];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_page_csv()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_page_csv() {
 | 
			
		||||
        $plist = period::findForPage($this);
 | 
			
		||||
 | 
			
		||||
        $model = $this->editor_model();
 | 
			
		||||
 | 
			
		||||
        $periods = intval($model["periods"]);
 | 
			
		||||
        // First line
 | 
			
		||||
        // First line.
 | 
			
		||||
        $csv = "\"\"";
 | 
			
		||||
        for($i = 1; $i <= $periods; $i++){
 | 
			
		||||
        for($i = 1; $i <= $periods; $i++) {
 | 
			
		||||
            $name = $plist[$i]->shortname();
 | 
			
		||||
            $csv .= ",\"{$name}\"";
 | 
			
		||||
            $csv .= ", \"{$name}\"";
 | 
			
		||||
        }
 | 
			
		||||
        $csv .= "\r\n";
 | 
			
		||||
        // next, make one line per studyline
 | 
			
		||||
        foreach($model["studylines"] as $line){
 | 
			
		||||
            // determine how many fields are simultaneous in the line at maximum
 | 
			
		||||
        // next, make one line per studyline.
 | 
			
		||||
        foreach ($model["studylines"] as $line) {
 | 
			
		||||
            // determine how many fields are simultaneous in the line at maximum.
 | 
			
		||||
            $maxlines = 1;
 | 
			
		||||
            for($i = 1; $i <= $periods; $i++){
 | 
			
		||||
                if(count($line["slots"]) > $i){
 | 
			
		||||
            for($i = 1; $i <= $periods; $i++) {
 | 
			
		||||
                if (count($line["slots"]) > $i) {
 | 
			
		||||
                    $ct = 0;
 | 
			
		||||
                    foreach($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm){
 | 
			
		||||
                        if($itm["type"] == "course"){
 | 
			
		||||
                    foreach ($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm) {
 | 
			
		||||
                        if ($itm["type"] == "course") {
 | 
			
		||||
                            $ct += 1;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if($ct > $maxlines){
 | 
			
		||||
                    if ($ct > $maxlines) {
 | 
			
		||||
                        $maxlines = $ct;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            for($lct = 0; $lct < $maxlines; $lct++){
 | 
			
		||||
            for($lct = 0; $lct < $maxlines; $lct++) {
 | 
			
		||||
                $csv .= "\"{$line["name"]}\"";
 | 
			
		||||
                for($i = 1; $i <= $periods; $i++){
 | 
			
		||||
                for($i = 1; $i <= $periods; $i++) {
 | 
			
		||||
                    $filled = false;
 | 
			
		||||
                    if(count($line["slots"]) > $i){
 | 
			
		||||
                    if (count($line["slots"]) > $i) {
 | 
			
		||||
                        $ct = 0;
 | 
			
		||||
                        foreach($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm){
 | 
			
		||||
                            if($itm["type"] == "course"){
 | 
			
		||||
                                if($ct == $lct){
 | 
			
		||||
                                    $csv .= ",\"";
 | 
			
		||||
                        foreach ($line["slots"][$i][studyline::SLOTSET_COMPETENCY] as $itm) {
 | 
			
		||||
                            if ($itm["type"] == "course") {
 | 
			
		||||
                                if ($ct == $lct) {
 | 
			
		||||
                                    $csv .= ", \"";
 | 
			
		||||
                                    $csv .= $itm["course"]["fullname"];
 | 
			
		||||
                                    $csv .= "\r\n";
 | 
			
		||||
                                    $first = true;
 | 
			
		||||
                                    foreach($itm["course"]["grades"] as $g){
 | 
			
		||||
                                        if($g["selected"]){
 | 
			
		||||
                                            if($first){
 | 
			
		||||
                                    foreach ($itm["course"]["grades"] as $g) {
 | 
			
		||||
                                        if ($g["selected"]) {
 | 
			
		||||
                                            if ($first) {
 | 
			
		||||
                                                $first = false;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            else{
 | 
			
		||||
| 
						 | 
				
			
			@ -370,8 +383,8 @@ class studyplanpage {
 | 
			
		|||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if(!$filled) {
 | 
			
		||||
                        $csv .= ",\"\"";
 | 
			
		||||
                    if (!$filled) {
 | 
			
		||||
                        $csv .= ", \"\"";
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                $csv .= "\r\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -381,29 +394,28 @@ class studyplanpage {
 | 
			
		|||
        return [ "format" => "text/csv", "content" => $csv];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_studylines(){
 | 
			
		||||
    public function export_studylines() {
 | 
			
		||||
        $model = $this->export_studylines_model();
 | 
			
		||||
        $json = json_encode([
 | 
			
		||||
            "type"=>"studylines",
 | 
			
		||||
            "version"=>2.0,
 | 
			
		||||
            "studylines"=>$model,
 | 
			
		||||
            ],\JSON_PRETTY_PRINT);
 | 
			
		||||
            ], \JSON_PRETTY_PRINT);
 | 
			
		||||
        return [ "format" => "application/json", "content" => $json];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_periods(){
 | 
			
		||||
    public function export_periods() {
 | 
			
		||||
        $model = period::page_model($this);
 | 
			
		||||
        $json = json_encode([
 | 
			
		||||
            "type"=>"periods",
 | 
			
		||||
            "version"=>2.0,
 | 
			
		||||
            "perioddesc"=>$model,
 | 
			
		||||
            ],\JSON_PRETTY_PRINT);
 | 
			
		||||
            ], \JSON_PRETTY_PRINT);
 | 
			
		||||
        return [ "format" => "application/json", "content" => $json];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function export_model()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_model() {
 | 
			
		||||
        $model = [
 | 
			
		||||
            'fullname' => $this->r->fullname,
 | 
			
		||||
            'shortname' => $this->r->shortname,
 | 
			
		||||
| 
						 | 
				
			
			@ -417,26 +429,23 @@ class studyplanpage {
 | 
			
		|||
        return $model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function export_studylines_model()
 | 
			
		||||
    {
 | 
			
		||||
    public function export_studylines_model() {
 | 
			
		||||
        $children = studyline::find_page_children($this);
 | 
			
		||||
        $lines = [];
 | 
			
		||||
        foreach($children as $c)
 | 
			
		||||
        {
 | 
			
		||||
        foreach ($children as $c) {
 | 
			
		||||
            $lines[] = $c->export_model();
 | 
			
		||||
        }
 | 
			
		||||
        return $lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function import_periods($content,$format="application/json")
 | 
			
		||||
    {
 | 
			
		||||
        if($format != "application/json") { return false;}
 | 
			
		||||
        $content = json_decode($content,true);
 | 
			
		||||
        if($content["type"] == "periods" && $content["version"] >= 2.0){
 | 
			
		||||
    public function import_periods($content, $format="application/json") {
 | 
			
		||||
        if ($format != "application/json") { return false;}
 | 
			
		||||
        $content = json_decode($content, true);
 | 
			
		||||
        if ($content["type"] == "periods" && $content["version"] >= 2.0) {
 | 
			
		||||
            return $this->import_periods_model($content["perioddesc"]);
 | 
			
		||||
        }
 | 
			
		||||
        else if($content["type"] == "studyplanpage" && $content["version"] >= 2.0){
 | 
			
		||||
        else if ($content["type"] == "studyplanpage" && $content["version"] >= 2.0) {
 | 
			
		||||
            return $this->import_periods_model($content["page"]["perioddesc"]);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -444,17 +453,16 @@ class studyplanpage {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function import_studylines($content,$format="application/json")
 | 
			
		||||
    {
 | 
			
		||||
        if($format != "application/json") { return false;}
 | 
			
		||||
        $content = json_decode($content,true);
 | 
			
		||||
        if($content["type"] == "studylines" && $content["version"] >= 2.0){
 | 
			
		||||
    public function import_studylines($content, $format="application/json") {
 | 
			
		||||
        if ($format != "application/json") { return false;}
 | 
			
		||||
        $content = json_decode($content, true);
 | 
			
		||||
        if ($content["type"] == "studylines" && $content["version"] >= 2.0) {
 | 
			
		||||
            return $this->import_studylines_model($content["studylines"]);
 | 
			
		||||
        }
 | 
			
		||||
        else if($content["type"] == "studyplanpage" && $content["version"] >= 2.0){
 | 
			
		||||
        else if ($content["type"] == "studyplanpage" && $content["version"] >= 2.0) {
 | 
			
		||||
            return $this->import_studylines_model($content["page"]["studylines"]);
 | 
			
		||||
        }
 | 
			
		||||
        else if($content["type"] == "studyplan" && $content["version"] >= 2.0){
 | 
			
		||||
        else if ($content["type"] == "studyplan" && $content["version"] >= 2.0) {
 | 
			
		||||
            return $this->import_studylines_model($content["studyplan"]["pages"][0]["studylines"]);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
| 
						 | 
				
			
			@ -462,52 +470,51 @@ class studyplanpage {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected function find_studyline_by_shortname($shortname){
 | 
			
		||||
    protected function find_studyline_by_shortname($shortname) {
 | 
			
		||||
        $children = studyline::find_page_children($this);
 | 
			
		||||
        foreach($children as $l){
 | 
			
		||||
            if($shortname == $l->shortname()){
 | 
			
		||||
        foreach ($children as $l) {
 | 
			
		||||
            if ($shortname == $l->shortname()) {
 | 
			
		||||
                return $l;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function import_periods_model($model){
 | 
			
		||||
    public function import_periods_model($model) {
 | 
			
		||||
        $periods = period::findForPage($this);
 | 
			
		||||
        foreach($model as $pmodel){
 | 
			
		||||
        foreach ($model as $pmodel) {
 | 
			
		||||
            $pi = $pmodel["period"];
 | 
			
		||||
            if(array_key_exists($pi,$periods)){
 | 
			
		||||
            if (array_key_exists($pi, $periods)) {
 | 
			
		||||
                $periods[$pi]->edit($pmodel);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function import_studylines_model($model)
 | 
			
		||||
    {
 | 
			
		||||
        // First attempt to map each studyline model to an existing or new line
 | 
			
		||||
    public function import_studylines_model($model) {
 | 
			
		||||
        // First attempt to map each studyline model to an existing or new line.
 | 
			
		||||
        $line_map = [];
 | 
			
		||||
        foreach($model as $ix => $linemodel){
 | 
			
		||||
        foreach ($model as $ix => $linemodel) {
 | 
			
		||||
            $line = $this->find_studyline_by_shortname($linemodel["shortname"]);
 | 
			
		||||
            if(empty($line)){
 | 
			
		||||
            if (empty($line)) {
 | 
			
		||||
                $linemodel["page_id"] = $this->id;
 | 
			
		||||
                $line = studyline::add($linemodel);
 | 
			
		||||
            } else {
 | 
			
		||||
                //$line->edit($linemodel); // Update the line with the settings from the imported file
 | 
			
		||||
                //$line->edit($linemodel); // Update the line with the settings from the imported file.
 | 
			
		||||
            }
 | 
			
		||||
            $line_map[$ix] = $line;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // next, let each study line import the study items
 | 
			
		||||
        // next, let each study line import the study items.
 | 
			
		||||
        $itemtranslation = [];
 | 
			
		||||
        $connections = [];
 | 
			
		||||
        foreach($model as $ix => $linemodel){
 | 
			
		||||
            $line_map[$ix]->import_studyitems($linemodel["slots"],$itemtranslation,$connections);
 | 
			
		||||
        foreach ($model as $ix => $linemodel) {
 | 
			
		||||
            $line_map[$ix]->import_studyitems($linemodel["slots"], $itemtranslation, $connections);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Finally, create the links between the study items
 | 
			
		||||
        foreach($connections as $from => $dests){
 | 
			
		||||
            foreach($dests as $to){
 | 
			
		||||
                studyitemconnection::connect($from,$itemtranslation[$to]);
 | 
			
		||||
        // Finally, create the links between the study items.
 | 
			
		||||
        foreach ($connections as $from => $dests) {
 | 
			
		||||
            foreach ($dests as $to) {
 | 
			
		||||
                studyitemconnection::connect($from, $itemtranslation[$to]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -7,21 +27,20 @@ class success {
 | 
			
		|||
    private $success;
 | 
			
		||||
    private $msg;
 | 
			
		||||
 | 
			
		||||
    public static function success($msg=""){
 | 
			
		||||
        return new self(true,$msg);
 | 
			
		||||
    public static function success($msg="") {
 | 
			
		||||
        return new self(true, $msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function fail($msg=""){
 | 
			
		||||
        return new self(false,$msg);
 | 
			
		||||
    public static function fail($msg="") {
 | 
			
		||||
        return new self(false, $msg);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __construct($success,$msg){
 | 
			
		||||
    public function __construct($success, $msg) {
 | 
			
		||||
        $this->success = ($success)?true:false;
 | 
			
		||||
        $this->msg = $msg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function structure()
 | 
			
		||||
    {
 | 
			
		||||
    public static function structure() {
 | 
			
		||||
        return new \external_single_structure([
 | 
			
		||||
            "success" => new \external_value(PARAM_BOOL, 'operation completed succesfully'),
 | 
			
		||||
            "msg" => new \external_value(PARAM_TEXT, 'message'),
 | 
			
		||||
| 
						 | 
				
			
			@ -32,11 +51,11 @@ class success {
 | 
			
		|||
        return ["success" => $this->success, "msg"=> $this->msg];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function successful(){
 | 
			
		||||
    public function successful() {
 | 
			
		||||
        return $this->success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function msg(){
 | 
			
		||||
    public function msg() {
 | 
			
		||||
        return $this->msg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\task;
 | 
			
		||||
require_once($CFG->dirroot.'/course/externallib.php');
 | 
			
		||||
use local_treestudyplan\studyplan;
 | 
			
		||||
| 
						 | 
				
			
			@ -20,18 +41,18 @@ class autocohortsync extends \core\task\scheduled_task  {
 | 
			
		|||
     * Execute the task.
 | 
			
		||||
     */
 | 
			
		||||
    public function execute() {
 | 
			
		||||
        if(get_config("local_treestudyplan","csync_enable")){
 | 
			
		||||
        if (get_config("local_treestudyplan", "csync_enable")) {
 | 
			
		||||
            \mtrace("Automatic csync cascading enabled");
 | 
			
		||||
            $studyplans = studyplan::find_all();
 | 
			
		||||
 | 
			
		||||
            foreach($studyplans as $studyplan) {
 | 
			
		||||
                // Only process studyplans that have been marked for change because 
 | 
			
		||||
            foreach ($studyplans as $studyplan) {
 | 
			
		||||
                // Only process studyplans that have been marked for change because .
 | 
			
		||||
                // a cohort change has occurred or a course has been added....
 | 
			
		||||
                if($studyplan->has_csync_changed()){
 | 
			
		||||
                if ($studyplan->has_csync_changed()) {
 | 
			
		||||
                    \mtrace("Studyplan {$studyplan->shortname()} needs processing");
 | 
			
		||||
                    $enroller = new cascadecohortsync($studyplan);
 | 
			
		||||
                    $enroller->sync();
 | 
			
		||||
                    if(get_config("local_treestudyplan","csync_users")){
 | 
			
		||||
                    if (get_config("local_treestudyplan", "csync_users")) {
 | 
			
		||||
                        $userenroller = new cascadeusersync($studyplan);
 | 
			
		||||
                        $userenroller->sync();
 | 
			
		||||
                    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,31 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan\task;
 | 
			
		||||
require_once($CFG->dirroot.'/course/externallib.php');
 | 
			
		||||
use local_treestudyplan\teachingfinder;
 | 
			
		||||
 | 
			
		||||
class refreshteacherlist extends \core\task\scheduled_task  {
 | 
			
		||||
    const CACHE_TIME = 4 * 60 * 60; // 2 hours
 | 
			
		||||
    const CACHE_TIME = 4 * 60 * 60; // 2 hours.
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the task's name as shown in admin screens.
 | 
			
		||||
| 
						 | 
				
			
			@ -21,9 +42,9 @@ class refreshteacherlist extends \core\task\scheduled_task  {
 | 
			
		|||
    public function execute() {
 | 
			
		||||
        \mtrace("Ververs lijst met leraren");
 | 
			
		||||
        $teacher_ids = teachingfinder::list_teacher_ids();
 | 
			
		||||
        foreach($teacher_ids as $tid){
 | 
			
		||||
        foreach ($teacher_ids as $tid) {
 | 
			
		||||
            $utime = teachingfinder::get_update_time($tid);
 | 
			
		||||
            if(time() - $utime > self::CACHE_TIME){
 | 
			
		||||
            if (time() - $utime > self::CACHE_TIME) {
 | 
			
		||||
                \mtrace("Teacher {$tid} is due for a refresh of the list");
 | 
			
		||||
                teachingfinder::update_teaching_cache($tid);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,22 +1,43 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
class teachingfinder {
 | 
			
		||||
    const TABLE = "local_treestudyplan_teachers";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static function list_my_plans(){
 | 
			
		||||
        global $USER,$DB;
 | 
			
		||||
    public static function list_my_plans() {
 | 
			
		||||
        global $USER, $DB;
 | 
			
		||||
        $userid = $USER->id;
 | 
			
		||||
 | 
			
		||||
        $records = $DB->get_records(self::TABLE,['teacher_id' => $userid]);
 | 
			
		||||
        if(count($records) == 0){
 | 
			
		||||
            // initiate a search if the cache is empty
 | 
			
		||||
        $records = $DB->get_records(self::TABLE, ['teacher_id' => $userid]);
 | 
			
		||||
        if (count($records) == 0) {
 | 
			
		||||
            // initiate a search if the cache is empty.
 | 
			
		||||
            self::update_teaching_cache($userid);
 | 
			
		||||
             $DB->get_records(self::TABLE,['teacher_id' => $userid]);
 | 
			
		||||
             $DB->get_records(self::TABLE, ['teacher_id' => $userid]);
 | 
			
		||||
        }
 | 
			
		||||
        $list = [];
 | 
			
		||||
        foreach($records as $r){
 | 
			
		||||
        foreach ($records as $r) {
 | 
			
		||||
            $list[] = studyplan::findById($r->studyplan_id);
 | 
			
		||||
        }
 | 
			
		||||
        return $list;
 | 
			
		||||
| 
						 | 
				
			
			@ -28,56 +49,55 @@ class teachingfinder {
 | 
			
		|||
     * (Has the  mod/assign::grade capability in one of the linked courses)
 | 
			
		||||
     * TODO: OPTIMIZE THIS CHECK!!!
 | 
			
		||||
     */
 | 
			
		||||
    public static function update_teaching_cache($userid){
 | 
			
		||||
    public static function update_teaching_cache($userid) {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        $list = [];
 | 
			
		||||
 | 
			
		||||
        // First find all active study plans
 | 
			
		||||
        // First find all active study plans.
 | 
			
		||||
 | 
			
		||||
        $sql = "SELECT p.id FROM {local_treestudyplan_page} p
 | 
			
		||||
            WHERE startdate <= NOW() and enddate >= NOW()";
 | 
			
		||||
        $page_ids = $DB->get_fieldset_sql($sql, []);
 | 
			
		||||
 | 
			
		||||
        // then parse them to see if the user has the grading permission in any of them 
 | 
			
		||||
        // (Which would make them a teacher for all intents and purposes)
 | 
			
		||||
        // then parse them to see if the user has the grading permission in any of them .
 | 
			
		||||
        // (Which would make them a teacher for all intents and purposes).
 | 
			
		||||
 | 
			
		||||
        foreach($page_ids as $page_id) {
 | 
			
		||||
        foreach ($page_ids as $page_id) {
 | 
			
		||||
            $sql = "SELECT i.course_id FROM {local_treestudyplan_item} i
 | 
			
		||||
                    INNER JOIN {local_treestudyplan_line} l ON i.line_id = l.id
 | 
			
		||||
                    WHERE l.page_id = :page_id AND i.course_id IS NOT NULL";
 | 
			
		||||
            $course_ids = $DB->get_fieldset_sql($sql, ["page_id" => $page_id]);
 | 
			
		||||
 | 
			
		||||
            $linked = false;
 | 
			
		||||
            foreach($course_ids as $cid){
 | 
			
		||||
            foreach ($course_ids as $cid) {
 | 
			
		||||
                $coursecontext =  \context_course::instance($cid);
 | 
			
		||||
                if (is_enrolled($coursecontext, $userid, 'mod/assign:grade')){
 | 
			
		||||
                if (is_enrolled($coursecontext, $userid, 'mod/assign:grade')) {
 | 
			
		||||
                    $linked = true;
 | 
			
		||||
                    break; // No need to search further
 | 
			
		||||
                    break; // No need to search further.
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if($linked)
 | 
			
		||||
            {
 | 
			
		||||
            if ($linked) {
 | 
			
		||||
                $list[] = $page_id;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Now, clear the database of all records for this user
 | 
			
		||||
        $DB->delete_records(self::TABLE,["teacher_id"=>$userid]);
 | 
			
		||||
        // And add new records for the found studyplans
 | 
			
		||||
        // Now, clear the database of all records for this user.
 | 
			
		||||
        $DB->delete_records(self::TABLE, ["teacher_id"=>$userid]);
 | 
			
		||||
        // And add new records for the found studyplans.
 | 
			
		||||
        $now = time();
 | 
			
		||||
        foreach($list as $page_id){
 | 
			
		||||
            // Retrieve the studyplan id from the page
 | 
			
		||||
            //TODO: Change this when page management is implemented to return the page instead of the plan
 | 
			
		||||
            $planid = $DB->get_field("local_treestudyplan_page","studyplan_id",["id" => $page_id]);
 | 
			
		||||
            $DB->insert_record(self::TABLE,["teacher_id"=>$userid,"studyplan_id"=>$planid,"update_time"=>$now]);
 | 
			
		||||
        foreach ($list as $page_id) {
 | 
			
		||||
            // Retrieve the studyplan id from the page.
 | 
			
		||||
            //TODO: Change this when page management is implemented to return the page instead of the plan.
 | 
			
		||||
            $planid = $DB->get_field("local_treestudyplan_page", "studyplan_id", ["id" => $page_id]);
 | 
			
		||||
            $DB->insert_record(self::TABLE, ["teacher_id"=>$userid, "studyplan_id"=>$planid, "update_time"=>$now]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function list_teacher_ids(){
 | 
			
		||||
    public static function list_teacher_ids() {
 | 
			
		||||
        global $DB;
 | 
			
		||||
        return $DB->get_fieldset_sql("SELECT DISTINCT teacher_id FROM {".self::TABLE."}");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
use \local_treestudyplan\local\gradegenerator;
 | 
			
		||||
| 
						 | 
				
			
			@ -39,13 +60,13 @@ if ($options['help']) {
 | 
			
		|||
    cli_writeln($usage);
 | 
			
		||||
    exit(2);
 | 
			
		||||
}
 | 
			
		||||
//cli_writeln(print_r($options,true));
 | 
			
		||||
//cli_writeln(print_r($options, true));.
 | 
			
		||||
 | 
			
		||||
if (empty($options['studyplan']) && empty($options["all"])) {
 | 
			
		||||
    cli_error('Missing mandatory argument studyplan.', 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(!empty($options["all"])){
 | 
			
		||||
if (!empty($options["all"])) {
 | 
			
		||||
    $plans = studyplan::find_all();
 | 
			
		||||
} else {
 | 
			
		||||
    $plans = studyplan::find_by_shortname($options["studyplan"]);
 | 
			
		||||
| 
						 | 
				
			
			@ -56,22 +77,22 @@ $generator = new gradegenerator();
 | 
			
		|||
$generator->fromFile($options["file"]);
 | 
			
		||||
 | 
			
		||||
cli_writeln(count($plans)." studyplans found:");
 | 
			
		||||
foreach($plans as $plan){
 | 
			
		||||
foreach ($plans as $plan) {
 | 
			
		||||
    cli_heading($plan->name());
 | 
			
		||||
    $users = $plan->find_linked_users();
 | 
			
		||||
    foreach($users as $u){
 | 
			
		||||
    foreach ($users as $u) {
 | 
			
		||||
        $generator->addstudent($u->username);
 | 
			
		||||
        $generator->addUserNameInfo($u->username,$u->firstname,$u->lastname);
 | 
			
		||||
        $generator->addUserNameInfo($u->username, $u->firstname, $u->lastname);
 | 
			
		||||
        cli_writeln("  - {$u->firstname} {$u->lastname} / {$u->username}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    foreach($plan->pages() as $page){
 | 
			
		||||
    foreach ($plan->pages() as $page) {
 | 
			
		||||
        $lines = studyline::find_page_children($page);
 | 
			
		||||
        foreach($lines as $line){
 | 
			
		||||
        foreach ($lines as $line) {
 | 
			
		||||
            cli_writeln("  ** {$line->name()} **");
 | 
			
		||||
            $items = studyitem::find_studyline_children($line);
 | 
			
		||||
            foreach($users as $u){
 | 
			
		||||
                $generator->addskill($u->username,$line->shortname());
 | 
			
		||||
            foreach ($users as $u) {
 | 
			
		||||
                $generator->addskill($u->username, $line->shortname());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
use \local_treestudyplan\local\gradegenerator;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +67,7 @@ if ($options['help']) {
 | 
			
		|||
    exit(2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/////////////////////////////////
 | 
			
		||||
/////////////////////////////////.
 | 
			
		||||
$user = get_admin();
 | 
			
		||||
 | 
			
		||||
if (!$user) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,14 +86,14 @@ login_attempt_valid($user);
 | 
			
		|||
complete_user_login($user);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
////////////////////////////////
 | 
			
		||||
////////////////////////////////.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if (empty($options['studyplan']) && empty($options["all"])) {
 | 
			
		||||
    cli_error('Missing mandatory argument studyplan.', 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if(!empty($options["all"])){
 | 
			
		||||
if (!empty($options["all"])) {
 | 
			
		||||
    $plans =  studyplan::find_all();
 | 
			
		||||
} else {
 | 
			
		||||
    $plans = studyplan::find_by_shortname($options["studyplan"]);
 | 
			
		||||
| 
						 | 
				
			
			@ -85,27 +106,27 @@ $generator->fromFile($options["file"]);
 | 
			
		|||
$assignments = [];
 | 
			
		||||
 | 
			
		||||
cli_writeln(count($plans)." studyplans found:");
 | 
			
		||||
foreach($plans as $plan){
 | 
			
		||||
foreach ($plans as $plan) {
 | 
			
		||||
    cli_heading($plan->name());
 | 
			
		||||
    $users = $plan->find_linked_users();
 | 
			
		||||
 | 
			
		||||
    foreach($plan->pages() as $page){
 | 
			
		||||
    foreach ($plan->pages() as $page) {
 | 
			
		||||
        $lines = studyline::find_page_children($page);
 | 
			
		||||
        foreach($lines as $line){
 | 
			
		||||
        foreach ($lines as $line) {
 | 
			
		||||
            cli_writeln("  ** {$line->name()} **");
 | 
			
		||||
            $items = studyitem::find_studyline_children($line);
 | 
			
		||||
            foreach($items as $item){
 | 
			
		||||
                if($item->type() == studyitem::COURSE) { // only handle courses for now
 | 
			
		||||
            foreach ($items as $item) {
 | 
			
		||||
                if ($item->type() == studyitem::COURSE) { // only handle courses for now.
 | 
			
		||||
                    $courseinfo = $item->getcourseinfo();
 | 
			
		||||
                    cli_writeln("    # {$courseinfo->shortname()}");
 | 
			
		||||
 | 
			
		||||
                    if($courseinfo->course()->startdate <= time()){
 | 
			
		||||
                    if ($courseinfo->course()->startdate <= time()) {
 | 
			
		||||
 | 
			
		||||
                        foreach($users as $u){
 | 
			
		||||
                        foreach ($users as $u) {
 | 
			
		||||
                            cli_writeln("      -> {$u->firstname} {$u->lastname} <-");
 | 
			
		||||
                            $gradables = gradeinfo::list_studyitem_gradables($item);
 | 
			
		||||
                            $gen = $generator->generate($u->username,$line->shortname(),$gradables);
 | 
			
		||||
                            foreach($gen as $gg){
 | 
			
		||||
                            $gen = $generator->generate($u->username, $line->shortname(), $gradables);
 | 
			
		||||
                            foreach ($gen as $gg) {
 | 
			
		||||
                                $g = $gg->gi;
 | 
			
		||||
                                $gi = $g->getGradeitem();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -113,36 +134,36 @@ foreach($plans as $plan){
 | 
			
		|||
                                $grade = $gg->gradetext;
 | 
			
		||||
                                cli_write ("        - {$name} = {$grade}");
 | 
			
		||||
 | 
			
		||||
                                // Check if the item is alreaady graded for this user
 | 
			
		||||
                                $existing = $count = $DB->count_records_select('grade_grades','itemid = :gradeitemid AND finalgrade IS NOT NULL and userid = :userid',
 | 
			
		||||
                                // Check if the item is alreaady graded for this user.
 | 
			
		||||
                                $existing = $count = $DB->count_records_select('grade_grades', 'itemid = :gradeitemid AND finalgrade IS NOT NULL and userid = :userid',
 | 
			
		||||
                                    ['gradeitemid' => $gi->id, 'userid' => $u->id]);
 | 
			
		||||
 | 
			
		||||
                                if(!$existing){
 | 
			
		||||
                                    if($gg->grade > 0){
 | 
			
		||||
                                        if($gi->itemmodule == "assign"){
 | 
			
		||||
                                            // If it is an assignment, submit though that interface 
 | 
			
		||||
                                            list($c,$cminfo) = get_course_and_cm_from_instance($gi->iteminstance,$gi->itemmodule);
 | 
			
		||||
                                if (!$existing) {
 | 
			
		||||
                                    if ($gg->grade > 0) {
 | 
			
		||||
                                        if ($gi->itemmodule == "assign") {
 | 
			
		||||
                                            // If it is an assignment, submit though that interface .
 | 
			
		||||
                                            list($c, $cminfo) = get_course_and_cm_from_instance($gi->iteminstance, $gi->itemmodule);
 | 
			
		||||
                                            $cm_ctx = \context_module::instance($cminfo->id);
 | 
			
		||||
                                            $a = new \assign($cm_ctx,$cminfo,$c);
 | 
			
		||||
                                            $a = new \assign($cm_ctx, $cminfo, $c);
 | 
			
		||||
 | 
			
		||||
                                            $ug = $a->get_user_grade($u->id,true);
 | 
			
		||||
                                            $ug = $a->get_user_grade($u->id, true);
 | 
			
		||||
                                            $ug->grade = grade_floatval($gg->grade);
 | 
			
		||||
                                            $ug->grader = $USER->id;
 | 
			
		||||
                                            $ug->feedbacktext = nl2br( htmlspecialchars($gg->fb));
 | 
			
		||||
                                            $ug->feedbackformat = FORMAT_HTML;
 | 
			
		||||
 | 
			
		||||
                                            //print_r($ug);
 | 
			
		||||
                                            if(!$options["dryrun"]){
 | 
			
		||||
                                            //print_r($ug);.
 | 
			
		||||
                                            if (!$options["dryrun"]) {
 | 
			
		||||
                                                $a->update_grade($ug);
 | 
			
		||||
 | 
			
		||||
                                                grade_regrade_final_grades($c->id,$u->id,$gi);
 | 
			
		||||
                                                grade_regrade_final_grades($c->id, $u->id, $gi);
 | 
			
		||||
                                                cli_writeln(" ... Stored");
 | 
			
		||||
                                            } else {
 | 
			
		||||
                                                cli_writeln(" ... (Dry Run)");
 | 
			
		||||
                                            }
 | 
			
		||||
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            // Otherwise, set the grade through the manual grading override
 | 
			
		||||
                                            // Otherwise, set the grade through the manual grading override.
 | 
			
		||||
                                            cli_writeln(" ... Cannot store");
 | 
			
		||||
 | 
			
		||||
                                        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
$capabilities = [
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										690
									
								
								db/services.php
									
									
									
									
									
								
							
							
						
						
									
										690
									
								
								db/services.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
$services = [
 | 
			
		||||
    "Competency listing" => [
 | 
			
		||||
| 
						 | 
				
			
			@ -36,527 +56,527 @@ $functions = [
 | 
			
		|||
    /***************************
 | 
			
		||||
     * Studyplan functions
 | 
			
		||||
     ***************************/
 | 
			
		||||
    'local_treestudyplan_list_studyplans' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_studyplans',          //external function name
 | 
			
		||||
        'description'   => 'List available studyplans',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_studyplans' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_studyplans',          //external function name.
 | 
			
		||||
        'description'   => 'List available studyplans',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan, local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan, local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_studyplan_map' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_studyplan_map',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve studyplan map',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_studyplan_map' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_studyplan_map',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve studyplan map',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan, local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan, local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_get_studyline_map' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_studyline_map',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve studyline map',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_studyline_map' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_studyline_map',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve studyline map',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_add_studyplan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'add_studyplan',          //external function name
 | 
			
		||||
        'description'   => 'Add studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_add_studyplan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'add_studyplan',          //external function name.
 | 
			
		||||
        'description'   => 'Add studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_add_studyline' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'add_studyline',          //external function name
 | 
			
		||||
        'description'   => 'Add studyline',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_add_studyline' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'add_studyline',          //external function name.
 | 
			
		||||
        'description'   => 'Add studyline',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_edit_studyplan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'edit_studyplan',          //external function name
 | 
			
		||||
        'description'   => 'Edit studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_edit_studyplan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'edit_studyplan',          //external function name.
 | 
			
		||||
        'description'   => 'Edit studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_edit_studyline' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'edit_studyline',          //external function name
 | 
			
		||||
        'description'   => 'Edit studyline',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_edit_studyline' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'edit_studyline',          //external function name.
 | 
			
		||||
        'description'   => 'Edit studyline',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_delete_studyplan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'delete_studyplan',          //external function name
 | 
			
		||||
        'description'   => 'Delete studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_delete_studyplan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'delete_studyplan',          //external function name.
 | 
			
		||||
        'description'   => 'Delete studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_delete_studyline' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'delete_studyline',          //external function name
 | 
			
		||||
        'description'   => 'Delete studyline',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_delete_studyline' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'delete_studyline',          //external function name.
 | 
			
		||||
        'description'   => 'Delete studyline',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_reorder_studylines' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'reorder_studylines',          //external function name
 | 
			
		||||
        'description'   => 'Reorder studylines',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_reorder_studylines' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'reorder_studylines',          //external function name.
 | 
			
		||||
        'description'   => 'Reorder studylines',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_get_studyitem' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_studyitem',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve study item',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_studyitem' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_studyitem',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve study item',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_add_studyitem' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'add_studyitem',          //external function name
 | 
			
		||||
        'description'   => 'Add study item',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_add_studyitem' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'add_studyitem',          //external function name.
 | 
			
		||||
        'description'   => 'Add study item',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_edit_studyitem' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'edit_studyitem',          //external function name
 | 
			
		||||
        'description'   => 'Edit study item',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_edit_studyitem' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'edit_studyitem',          //external function name.
 | 
			
		||||
        'description'   => 'Edit study item',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_reorder_studyitems' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'reorder_studyitems',          //external function name
 | 
			
		||||
        'description'   => 'Reorder study items',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_reorder_studyitems' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'reorder_studyitems',          //external function name.
 | 
			
		||||
        'description'   => 'Reorder study items',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_delete_studyitem' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'delete_studyitem',          //external function name
 | 
			
		||||
        'description'   => 'Delete study item',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_delete_studyitem' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'delete_studyitem',          //external function name.
 | 
			
		||||
        'description'   => 'Delete study item',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_connect_studyitems' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'connect_studyitems',          //external function name
 | 
			
		||||
        'description'   => 'Connect study items',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_connect_studyitems' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'connect_studyitems',          //external function name.
 | 
			
		||||
        'description'   => 'Connect study items',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_disconnect_studyitems' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'disconnect_studyitems',          //external function name
 | 
			
		||||
        'description'   => 'Disconnect study items',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_disconnect_studyitems' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'disconnect_studyitems',          //external function name.
 | 
			
		||||
        'description'   => 'Disconnect study items',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    /***************************
 | 
			
		||||
     * Badge functions
 | 
			
		||||
     ***************************/
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_list_badges' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_badges',          //external function name
 | 
			
		||||
        'description'   => 'List availabel site badges',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_badges' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_badges',          //external function name.
 | 
			
		||||
        'description'   => 'List availabel site badges',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    /***************************
 | 
			
		||||
     * Association functions
 | 
			
		||||
     ***************************/
 | 
			
		||||
    'local_treestudyplan_list_cohort' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_cohort',          //external function name
 | 
			
		||||
        'description'   => 'List available cohorts',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_cohort' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_cohort',          //external function name.
 | 
			
		||||
        'description'   => 'List available cohorts',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_find_user' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'find_user',          //external function name
 | 
			
		||||
        'description'   => 'Find user',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_find_user' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'find_user',          //external function name.
 | 
			
		||||
        'description'   => 'Find user',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_connect_cohort' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'connect_cohort',          //external function name
 | 
			
		||||
        'description'   => 'Connect cohort to studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_connect_cohort' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'connect_cohort',          //external function name.
 | 
			
		||||
        'description'   => 'Connect cohort to studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_disconnect_cohort' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'disconnect_cohort',          //external function name
 | 
			
		||||
        'description'   => 'Disconnect cohort from study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_disconnect_cohort' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'disconnect_cohort',          //external function name.
 | 
			
		||||
        'description'   => 'Disconnect cohort from study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'local_treestudyplan_connect_user' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'connect_user',          //external function name
 | 
			
		||||
        'description'   => 'Connect user to study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_connect_user' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'connect_user',          //external function name.
 | 
			
		||||
        'description'   => 'Connect user to study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_disconnect_user' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'disconnect_user',          //external function name
 | 
			
		||||
        'description'   => 'Disconnect user from studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_disconnect_user' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'disconnect_user',          //external function name.
 | 
			
		||||
        'description'   => 'Disconnect user from studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_associated_users' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'associated_users',          //external function name
 | 
			
		||||
        'description'   => 'List users associated with a studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_associated_users' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'associated_users',          //external function name.
 | 
			
		||||
        'description'   => 'List users associated with a studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_associated_cohorts' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'associated_cohorts',          //external function name
 | 
			
		||||
        'description'   => 'List cohorts associated with a studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_associated_cohorts' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'associated_cohorts',          //external function name.
 | 
			
		||||
        'description'   => 'List cohorts associated with a studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_list_user_studyplans' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_user_studyplans',          //external function name
 | 
			
		||||
        'description'   => 'List user studyplans',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_user_studyplans' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_user_studyplans',          //external function name.
 | 
			
		||||
        'description'   => 'List user studyplans',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_user_studyplans' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_user_studyplans',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve user studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_user_studyplans' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_user_studyplans',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve user studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_user_studyplan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_user_studyplan',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve user studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_user_studyplan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_user_studyplan',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve user studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_invited_studyplan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_invited_studyplan',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve user studyplan based on invite',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_invited_studyplan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_invited_studyplan',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve user studyplan based on invite',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => '', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => '', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => false,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_list_own_studyplans' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_own_studyplans',          //external function name
 | 
			
		||||
        'description'   => 'List own studyplans',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_own_studyplans' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_own_studyplans',          //external function name.
 | 
			
		||||
        'description'   => 'List own studyplans',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => '', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => '', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_own_studyplan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_own_studyplan',          //external function name
 | 
			
		||||
        'description'   => 'Retrieve own studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_own_studyplan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_own_studyplan',          //external function name.
 | 
			
		||||
        'description'   => 'Retrieve own studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => '', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => '', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_map_categories' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'map_categories',          //external function name
 | 
			
		||||
        'description'   => 'List available root categories',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_map_categories' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'map_categories',          //external function name.
 | 
			
		||||
        'description'   => 'List available root categories',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_category' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_category',          //external function name
 | 
			
		||||
        'description'   => 'Get details for specified category',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_category' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_category',          //external function name.
 | 
			
		||||
        'description'   => 'Get details for specified category',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_include_grade' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'include_grade',          //external function name
 | 
			
		||||
        'description'   => 'Include gradable in result',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_include_grade' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'include_grade',          //external function name.
 | 
			
		||||
        'description'   => 'Include gradable in result',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan, local/treestudyplan:selectowngradables', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan, local/treestudyplan:selectowngradables', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_all_associated' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'all_associated',          //external function name
 | 
			
		||||
        'description'   => 'List associated users',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_all_associated' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'all_associated',          //external function name.
 | 
			
		||||
        'description'   => 'List associated users',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_list_aggregators' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_aggregators',          //external function name
 | 
			
		||||
        'description'   => 'List available aggregators',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_aggregators' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_aggregators',          //external function name.
 | 
			
		||||
        'description'   => 'List available aggregators',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_disable_autoenddate' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'disable_autoenddate',          //external function name
 | 
			
		||||
        'description'   => 'Disable automatic end dates on courses in the study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_disable_autoenddate' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'disable_autoenddate',          //external function name.
 | 
			
		||||
        'description'   => 'Disable automatic end dates on courses in the study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_force_studyplan_scale' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'force_studyplan_scale',          //external function name
 | 
			
		||||
        'description'   => 'Change all associated gradables to the chosen scale if possible',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_force_studyplan_scale' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'force_studyplan_scale',          //external function name.
 | 
			
		||||
        'description'   => 'Change all associated gradables to the chosen scale if possible',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_list_scales' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_scales',          //external function name
 | 
			
		||||
        'description'   => 'List system scales',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_scales' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_scales',          //external function name.
 | 
			
		||||
        'description'   => 'List system scales',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:forcescales', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_duplicate_plan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'duplicate_plan',          //external function name
 | 
			
		||||
        'description'   => 'Copy studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_duplicate_plan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'duplicate_plan',          //external function name.
 | 
			
		||||
        'description'   => 'Copy studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_export_plan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'export_plan',          //external function name
 | 
			
		||||
        'description'   => 'Export study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_export_plan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'export_plan',          //external function name.
 | 
			
		||||
        'description'   => 'Export study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_export_studylines' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'export_studylines',          //external function name
 | 
			
		||||
        'description'   => 'Export study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_export_studylines' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'export_studylines',          //external function name.
 | 
			
		||||
        'description'   => 'Export study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_import_plan' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'import_plan',          //external function name
 | 
			
		||||
        'description'   => 'Import study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_import_plan' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'import_plan',          //external function name.
 | 
			
		||||
        'description'   => 'Import study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_import_studylines' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'import_studylines',          //external function name
 | 
			
		||||
        'description'   => 'Import study plan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_import_studylines' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'import_studylines',          //external function name.
 | 
			
		||||
        'description'   => 'Import study plan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_edit_period' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'edit_period',          //external function name
 | 
			
		||||
        'description'   => 'Edit period name and timing',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_edit_period' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'edit_period',          //external function name.
 | 
			
		||||
        'description'   => 'Edit period name and timing',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_submit_cm_editform' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'submit_cm_editform',          //external function name
 | 
			
		||||
        'description'   => 'Submit course module edit form',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_submit_cm_editform' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'submit_cm_editform',          //external function name.
 | 
			
		||||
        'description'   => 'Submit course module edit form',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'write',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_get_teaching_studyplans' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'get_teaching_studyplans',          //external function name
 | 
			
		||||
        'description'   => 'Get the studyplans I currently teach in',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_get_teaching_studyplans' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studentstudyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'get_teaching_studyplans',          //external function name.
 | 
			
		||||
        'description'   => 'Get the studyplans I currently teach in',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_list_accessible_categories' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_accessible_categories',          //external function name
 | 
			
		||||
        'description'   => 'Get categories accessible to the current user',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
    'local_treestudyplan_list_accessible_categories' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_accessible_categories',          //external function name.
 | 
			
		||||
        'description'   => 'Get categories accessible to the current user',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_list_used_categories' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'list_used_categories',          //external function name
 | 
			
		||||
        'description'   => 'Get categories hosting  a studyplan',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
    'local_treestudyplan_list_used_categories' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'list_used_categories',          //external function name.
 | 
			
		||||
        'description'   => 'Get categories hosting  a studyplan',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_scan_badge_progress' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'scan_badge_progress',          //external function name
 | 
			
		||||
        'description'   => 'Scan progress of students in attaining badge',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
    'local_treestudyplan_scan_badge_progress' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'scan_badge_progress',          //external function name.
 | 
			
		||||
        'description'   => 'Scan progress of students in attaining badge',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_scan_completion_progress' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'scan_completion_progress',          //external function name
 | 
			
		||||
        'description'   => 'Scan progress of students in attaining completions',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
    'local_treestudyplan_scan_completion_progress' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'scan_completion_progress',          //external function name.
 | 
			
		||||
        'description'   => 'Scan progress of students in attaining completions',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_scan_grade_progress' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'scan_grade_progress',          //external function name
 | 
			
		||||
        'description'   => 'Scan progress of students in attaining grades',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required
 | 
			
		||||
    'local_treestudyplan_scan_grade_progress' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\courseservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'scan_grade_progress',          //external function name.
 | 
			
		||||
        'description'   => 'Scan progress of students in attaining grades',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:viewuserreports', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_cascade_cohortsync' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'cascade_cohortsync',          //external function name
 | 
			
		||||
        'description'   => 'Sync cohortsync to studyplan association',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
    'local_treestudyplan_cascade_cohortsync' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\associationservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'cascade_cohortsync',          //external function name.
 | 
			
		||||
        'description'   => 'Sync cohortsync to studyplan association',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
    'local_treestudyplan_course_period_timing' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'course_period_timing',          //external function name
 | 
			
		||||
        'description'   => 'Chenge course start and end times to match period',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
    'local_treestudyplan_course_period_timing' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'course_period_timing',          //external function name.
 | 
			
		||||
        'description'   => 'Chenge course start and end times to match period',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
   'local_treestudyplan_set_studyitem_span' => [         //web service function name
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function
 | 
			
		||||
        'methodname'    => 'set_studyitem_span',          //external function name
 | 
			
		||||
        'description'   => 'Change the span of a course item',    //human readable description of the web service function
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write)
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required
 | 
			
		||||
   'local_treestudyplan_set_studyitem_span' => [         //web service function name.
 | 
			
		||||
        'classname'     => '\local_treestudyplan\studyplanservice',  //class containing the external function.
 | 
			
		||||
        'methodname'    => 'set_studyitem_span',          //external function name.
 | 
			
		||||
        'description'   => 'Change the span of a course item',    //human readable description of the web service function.
 | 
			
		||||
        'type'          => 'read',                  //database rights of the web service function (read, write).
 | 
			
		||||
        'capabilities'  => 'local/treestudyplan:editstudyplan', // Advises the admin which capabilities are required.
 | 
			
		||||
        'ajax'          => true,
 | 
			
		||||
        'loginrequired' => true,
 | 
			
		||||
    ],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										21
									
								
								db/tasks.php
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								db/tasks.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
$tasks = [
 | 
			
		||||
    [
 | 
			
		||||
        'classname' => 'local_treestudyplan\task\autocohortsync',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
function xmldb_local_treestudyplan_upgrade($oldversion) {
 | 
			
		||||
    global $DB;
 | 
			
		||||
    $dbman = $DB->get_manager();
 | 
			
		||||
| 
						 | 
				
			
			@ -266,9 +287,9 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
 | 
			
		|||
        if (!$dbman->table_exists($table)) {
 | 
			
		||||
            $dbman->create_table($table);
 | 
			
		||||
 | 
			
		||||
            // Create a page 0 with it's data copied
 | 
			
		||||
            // Create a page 0 with it's data copied.
 | 
			
		||||
            $plans = $DB->get_recordset("local_treestudyplan");
 | 
			
		||||
            foreach($plans as $p){
 | 
			
		||||
            foreach ($plans as $p) {
 | 
			
		||||
                $o = [  "studyplan_id" => $p->id,
 | 
			
		||||
                        "periods" => $p->slots,
 | 
			
		||||
                        "fullname" => $p->name,
 | 
			
		||||
| 
						 | 
				
			
			@ -277,17 +298,17 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
 | 
			
		|||
                        "startdate" => $p->startdate,
 | 
			
		||||
                        "enddate" => $p->enddate,
 | 
			
		||||
                ];
 | 
			
		||||
                $pageid = $DB->insert_record("local_treestudyplan_page",$o);
 | 
			
		||||
                $pageid = $DB->insert_record("local_treestudyplan_page", $o);
 | 
			
		||||
 | 
			
		||||
                // Now find all studyline references to the studyplan 
 | 
			
		||||
                // And update their record to reference the page instead of the plan
 | 
			
		||||
                $lines = $DB->get_recordset("local_treestudyplan_line",["studyplan_id" => $p->id]);
 | 
			
		||||
                foreach($lines as $l){
 | 
			
		||||
                // Now find all studyline references to the studyplan .
 | 
			
		||||
                // And update their record to reference the page instead of the plan.
 | 
			
		||||
                $lines = $DB->get_recordset("local_treestudyplan_line", ["studyplan_id" => $p->id]);
 | 
			
		||||
                foreach ($lines as $l) {
 | 
			
		||||
                    $lo = [
 | 
			
		||||
                        "id" => $l->id,
 | 
			
		||||
                        "page_id"=> $pageid,
 | 
			
		||||
                    ];
 | 
			
		||||
                    $DB->update_record("local_treestudyplan_line",$lo);
 | 
			
		||||
                    $DB->update_record("local_treestudyplan_line", $lo);
 | 
			
		||||
                }
 | 
			
		||||
                $lines->close();
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -347,16 +368,16 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
 | 
			
		|||
        if (!$dbman->table_exists($table)) {
 | 
			
		||||
            $dbman->create_table($table);
 | 
			
		||||
 | 
			
		||||
            // Create a period page for all slots in the study
 | 
			
		||||
            // Create a period page for all slots in the study.
 | 
			
		||||
            $recordset = $DB->get_recordset("local_treestudyplan_page");
 | 
			
		||||
            foreach($recordset as $r){
 | 
			
		||||
            foreach ($recordset as $r) {
 | 
			
		||||
 | 
			
		||||
                // Make a best guess for the periods based on the specified period for the plan and the 
 | 
			
		||||
                // Make a best guess for the periods based on the specified period for the plan and the .
 | 
			
		||||
                $pcount = $r->periods;
 | 
			
		||||
                $ystart = strtotime($r->startdate)+0;
 | 
			
		||||
                $yend = strtotime($r->enddate)+0;
 | 
			
		||||
                if($yend < $ystart){
 | 
			
		||||
                    // If no end time is given, assume a year duration for period calculations
 | 
			
		||||
                if ($yend < $ystart) {
 | 
			
		||||
                    // If no end time is given, assume a year duration for period calculations.
 | 
			
		||||
                    $ydelta = (365*24*60*60)/$pcount;
 | 
			
		||||
                }
 | 
			
		||||
                else{
 | 
			
		||||
| 
						 | 
				
			
			@ -364,19 +385,19 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
 | 
			
		|||
                }
 | 
			
		||||
                $ptime = $ydelta / $pcount;
 | 
			
		||||
 | 
			
		||||
                for($i=0; $i < $pcount; $i++){
 | 
			
		||||
                for($i=0; $i < $pcount; $i++) {
 | 
			
		||||
                    $pnum = $i+1;
 | 
			
		||||
                    $pstart = $ystart + ($i*$ptime);
 | 
			
		||||
                    $pend = ($pstart + $ptime)-(24*60*60); // minus one day
 | 
			
		||||
                    $pend = ($pstart + $ptime)-(24*60*60); // minus one day.
 | 
			
		||||
 | 
			
		||||
                    $o = [  "page_id" => $r->id,
 | 
			
		||||
                            "period" => $pnum,
 | 
			
		||||
                            "fullname" => "Period {$pnum}",
 | 
			
		||||
                            "shortname" => "P{$pnum}",
 | 
			
		||||
                            "startdate" => date("Y-m-d",$pstart),
 | 
			
		||||
                            "enddate" => date("Y-m-d",$pend)
 | 
			
		||||
                            "startdate" => date("Y-m-d", $pstart),
 | 
			
		||||
                            "enddate" => date("Y-m-d", $pend)
 | 
			
		||||
                    ];
 | 
			
		||||
                    $DB->insert_record("local_treestudyplan_period",$o);
 | 
			
		||||
                    $DB->insert_record("local_treestudyplan_period", $o);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $recordset->close();
 | 
			
		||||
| 
						 | 
				
			
			@ -458,8 +479,8 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
 | 
			
		|||
 | 
			
		||||
    if ($oldversion < 2023082100) {
 | 
			
		||||
 | 
			
		||||
        // Up to version 20230821 the studyplan_id field still existed in the install.xml file
 | 
			
		||||
        // Below code remedies that - even though this is a repeat of part of an earlier upgrade
 | 
			
		||||
        // Up to version 20230821 the studyplan_id field still existed in the install.xml file.
 | 
			
		||||
        // Below code remedies that - even though this is a repeat of part of an earlier upgrade.
 | 
			
		||||
 | 
			
		||||
        // Define field studyplan_id to be dropped from local_treestudyplan_line.
 | 
			
		||||
        $table = new xmldb_table('local_treestudyplan_line');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								doc.php
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								doc.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,11 +1,32 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/weblib.php');
 | 
			
		||||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/doc.php",array());
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/doc.php", array());
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
 | 
			
		||||
$PAGE->set_pagelayout('base');
 | 
			
		||||
$PAGE->set_context($systemcontext);
 | 
			
		||||
| 
						 | 
				
			
			@ -14,16 +35,15 @@ require_login();
 | 
			
		|||
$pi = $_SERVER['PATH_INFO'];
 | 
			
		||||
$file = $CFG->dirroot."/local/treestudyplan/doc".$pi;
 | 
			
		||||
 | 
			
		||||
// Fallback to index
 | 
			
		||||
if(!file_exists($file)){
 | 
			
		||||
// Fallback to index.
 | 
			
		||||
if (!file_exists($file)) {
 | 
			
		||||
	$file = $CFG->dirroot."/local/treestudyplan/doc/index.htm";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$mime = mime_content_type($file);
 | 
			
		||||
$text_types = ["text/html","text/plain"];
 | 
			
		||||
$text_types = ["text/html", "text/plain"];
 | 
			
		||||
 | 
			
		||||
if( in_array($mime,$text_types))
 | 
			
		||||
{
 | 
			
		||||
if ( in_array($mime, $text_types)) {
 | 
			
		||||
	print $OUTPUT->header();
 | 
			
		||||
	print file_get_contents($file);
 | 
			
		||||
	print $OUTPUT->footer();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,23 +1,32 @@
 | 
			
		|||
<?php
 | 
			
		||||
if(isset($_SERVER['SCRIPT_FILENAME']))
 | 
			
		||||
{
 | 
			
		||||
	// If SCRIPT_FILENAME is set, use that so the symlinked directories the developmen environment uses are handled correctly
 | 
			
		||||
	$root = dirname(dirname(dirname($_SERVER['SCRIPT_FILENAME'])));
 | 
			
		||||
	error_log("Using {$root}/config.php");
 | 
			
		||||
	require_once($root."/config.php");
 | 
			
		||||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
	// If not, assume the cwd is not symlinked and proceed as we are used to
 | 
			
		||||
	require_once("../../config.php");
 | 
			
		||||
}
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once("./lib.php");
 | 
			
		||||
require_once($CFG->libdir.'/weblib.php');
 | 
			
		||||
require_once($CFG->dirroot.'/local/treestudyplan/classes/reportinvite_form.php');
 | 
			
		||||
 | 
			
		||||
$add    = optional_param('add', '', PARAM_ALPHANUM);     // module name
 | 
			
		||||
$add    = optional_param('add', '', PARAM_ALPHANUM);     // module name.
 | 
			
		||||
$update = optional_param('update', 0, PARAM_INT);
 | 
			
		||||
$resend = optional_param('resend', 0, PARAM_INT);
 | 
			
		||||
$delete = optional_param('delete', 0, PARAM_INT);
 | 
			
		||||
| 
						 | 
				
			
			@ -28,8 +37,7 @@ $PAGE->set_url("/local/treestudyplan/edit-invite.php");
 | 
			
		|||
$PAGE->set_pagelayout('base');
 | 
			
		||||
$PAGE->set_context($systemcontext);
 | 
			
		||||
 | 
			
		||||
if($update > 0)
 | 
			
		||||
{
 | 
			
		||||
if ($update > 0) {
 | 
			
		||||
	$PAGE->set_title(get_string('invite_desc_edit', 'local_treestudyplan'));
 | 
			
		||||
	$PAGE->set_heading(get_string('invite_desc_edit', 'local_treestudyplan'));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -40,40 +48,37 @@ else
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Check if user has capability to manage study plan units
 | 
			
		||||
// Check if user has capability to manage study plan units.
 | 
			
		||||
require_login();
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->header();
 | 
			
		||||
 | 
			
		||||
if(!empty($add))
 | 
			
		||||
{
 | 
			
		||||
if (!empty($add)) {
 | 
			
		||||
	$data = array(
 | 
			
		||||
	 'add' => 1,
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
else if(!empty($update))
 | 
			
		||||
{
 | 
			
		||||
else if (!empty($update)) {
 | 
			
		||||
	$data = $DB->get_record("local_treestudyplan_invit", array('id' => $update));
 | 
			
		||||
	$data->update = $update;
 | 
			
		||||
	if(empty($data) || $data->user_id != $USER->id)
 | 
			
		||||
	if (empty($data) || $data->user_id != $USER->id)
 | 
			
		||||
	{
 | 
			
		||||
		print_error('invalidaction');
 | 
			
		||||
		exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
else if(!empty($resend))
 | 
			
		||||
{
 | 
			
		||||
else if (!empty($resend)) {
 | 
			
		||||
	$data = $DB->get_record("local_treestudyplan_invit", array('id' => $resend));
 | 
			
		||||
	$data->resend = $resend;
 | 
			
		||||
	if(empty($data) || $data->user_id != $USER->id)
 | 
			
		||||
	if (empty($data) || $data->user_id != $USER->id)
 | 
			
		||||
	{
 | 
			
		||||
		print_error('invalidaction');
 | 
			
		||||
		exit;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Do some resending of an invitation
 | 
			
		||||
	// Do some resending of an invitation.
 | 
			
		||||
	local_treestudyplan_send_invite($data->id);	
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
| 
						 | 
				
			
			@ -82,11 +87,10 @@ else if(!empty($resend))
 | 
			
		|||
	print $OUTPUT->footer();
 | 
			
		||||
	exit;
 | 
			
		||||
}
 | 
			
		||||
else if(!empty($delete))
 | 
			
		||||
{
 | 
			
		||||
else if (!empty($delete)) {
 | 
			
		||||
	$data = $DB->get_record("local_treestudyplan_invit", array('id' => $delete));
 | 
			
		||||
	$data->delete = $delete;
 | 
			
		||||
	if(empty($data) || $data->user_id != $USER->id)
 | 
			
		||||
	if (empty($data) || $data->user_id != $USER->id)
 | 
			
		||||
	{
 | 
			
		||||
		print_error('invalidaction');
 | 
			
		||||
		exit;
 | 
			
		||||
| 
						 | 
				
			
			@ -105,13 +109,11 @@ else {
 | 
			
		|||
$mform = new reportinvite_form();
 | 
			
		||||
$mform->set_data($data);
 | 
			
		||||
 | 
			
		||||
if($mform->is_cancelled())
 | 
			
		||||
{
 | 
			
		||||
if ($mform->is_cancelled()) {
 | 
			
		||||
	redirect("$CFG->wwwroot/local/treestudyplan/invitations.php");
 | 
			
		||||
}
 | 
			
		||||
else if ($data = $mform->get_data())
 | 
			
		||||
{
 | 
			
		||||
	if(!empty($data->update))
 | 
			
		||||
else if ($data = $mform->get_data()) {
 | 
			
		||||
	if (!empty($data->update))
 | 
			
		||||
	{
 | 
			
		||||
		
 | 
			
		||||
		$id = $data->update;
 | 
			
		||||
| 
						 | 
				
			
			@ -123,21 +125,21 @@ else if ($data = $mform->get_data())
 | 
			
		|||
		redirect("$CFG->wwwroot/local/treestudyplan/invitations.php");
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	else if(!empty($data->add))
 | 
			
		||||
	else if (!empty($data->add))
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		$id = $DB->insert_record("local_treestudyplan_invit",$data, true);
 | 
			
		||||
		$id = $DB->insert_record("local_treestudyplan_invit", $data, true);
 | 
			
		||||
		
 | 
			
		||||
		// Send invitaion mail
 | 
			
		||||
		// Send invitaion mail.
 | 
			
		||||
		local_treestudyplan_send_invite($id);	
 | 
			
		||||
 | 
			
		||||
		redirect("$CFG->wwwroot/local/treestudyplan/invitations.php?sent={$id}");
 | 
			
		||||
	}
 | 
			
		||||
	else if(!empty($data->resend))
 | 
			
		||||
	else if (!empty($data->resend))
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
	else if(!empty($data->delete))
 | 
			
		||||
	else if (!empty($data->delete))
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +154,7 @@ else if ($data = $mform->get_data())
 | 
			
		|||
else
 | 
			
		||||
{
 | 
			
		||||
	$data = null;
 | 
			
		||||
	if($unitid > 0)
 | 
			
		||||
	if ($unitid > 0)
 | 
			
		||||
	{
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/weblib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -7,20 +28,19 @@ use \local_treestudyplan\courseservice;
 | 
			
		|||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/edit-plan.php",array());
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/edit-plan.php", array());
 | 
			
		||||
require_login();
 | 
			
		||||
 | 
			
		||||
// Figure out the context (category or system, based on either category or context parameter)
 | 
			
		||||
$categoryid = optional_param('categoryid', 0, PARAM_INT); // Category id
 | 
			
		||||
$contextid = optional_param('contextid', 0, PARAM_INT); // Context id
 | 
			
		||||
if($categoryid > 0){
 | 
			
		||||
// Figure out the context (category or system, based on either category or context parameter).
 | 
			
		||||
$categoryid = optional_param('categoryid', 0, PARAM_INT); // Category id.
 | 
			
		||||
$contextid = optional_param('contextid', 0, PARAM_INT); // Context id.
 | 
			
		||||
if ($categoryid > 0) {
 | 
			
		||||
	$studyplancontext = context_coursecat::instance($categoryid);
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
elseif($contextid > 0)
 | 
			
		||||
{
 | 
			
		||||
else if ($contextid > 0) {
 | 
			
		||||
	$studyplancontext = context::instance_by_id($contextid);
 | 
			
		||||
	if(in_array($studyplancontext->contextlevel,[CONTEXT_SYSTEM,CONTEXT_COURSECAT]))
 | 
			
		||||
	if (in_array($studyplancontext->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT]))
 | 
			
		||||
	{
 | 
			
		||||
		$categoryid = $studyplancontext->instanceid;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,47 +51,47 @@ elseif($contextid > 0)
 | 
			
		|||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
	// If no context is selected, find the first available one
 | 
			
		||||
	// If no context is selected, find the first available one.
 | 
			
		||||
	$available_contexts = courseservice::list_accessible_categories_with_usage("edit");
 | 
			
		||||
	$contextid=1; // fallback to system context
 | 
			
		||||
	foreach($available_contexts as $ctx){
 | 
			
		||||
		if($ctx->count > 0){
 | 
			
		||||
	$contextid=1; // fallback to system context.
 | 
			
		||||
	foreach ($available_contexts as $ctx) {
 | 
			
		||||
		if ($ctx->count > 0) {
 | 
			
		||||
			$contextid = $ctx->ctxid;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// reload page with selected category
 | 
			
		||||
	$url = new \moodle_url('/local/treestudyplan/edit-plan.php',["contextid" => $contextid]);
 | 
			
		||||
	// reload page with selected category.
 | 
			
		||||
	$url = new \moodle_url('/local/treestudyplan/edit-plan.php', ["contextid" => $contextid]);
 | 
			
		||||
	header('Location: '.$url->out(false), true, 302);
 | 
			
		||||
	exit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
require_capability('local/treestudyplan:editstudyplan',$studyplancontext);
 | 
			
		||||
$contextname = $studyplancontext->get_context_name(false,false);
 | 
			
		||||
require_capability('local/treestudyplan:editstudyplan', $studyplancontext);
 | 
			
		||||
$contextname = $studyplancontext->get_context_name(false, false);
 | 
			
		||||
 | 
			
		||||
$PAGE->set_pagelayout('coursecategory');
 | 
			
		||||
$PAGE->set_context($studyplancontext);
 | 
			
		||||
$PAGE->set_title(get_string('cfg_plans','local_treestudyplan')." - ".$contextname);
 | 
			
		||||
$PAGE->set_title(get_string('cfg_plans', 'local_treestudyplan')." - ".$contextname);
 | 
			
		||||
$PAGE->set_heading($contextname);
 | 
			
		||||
 | 
			
		||||
if($studyplancontext->id > 1){
 | 
			
		||||
if ($studyplancontext->id > 1) {
 | 
			
		||||
	navigation_node::override_active_url(new moodle_url('/course/index.php', ['categoryid' => $categoryid ]));
 | 
			
		||||
	$PAGE->navbar->add(get_string('cfg_plans','local_treestudyplan'));
 | 
			
		||||
	$PAGE->navbar->add(get_string('cfg_plans', 'local_treestudyplan'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Load javascripts and specific css
 | 
			
		||||
// Load javascripts and specific css.
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-edit-plan', 'init', [$studyplancontext->id,$categoryid]);
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-edit-plan', 'init', [$studyplancontext->id, $categoryid]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$catlist = courseservice::list_accessible_categories_with_usage("edit");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//Local translate function
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan'){
 | 
			
		||||
	print get_string($str,$plugin,$param);
 | 
			
		||||
//Local translate function.
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan') {
 | 
			
		||||
	print get_string($str, $plugin, $param);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->header();
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +110,7 @@ print $OUTPUT->header();
 | 
			
		|||
			<b-form-select text='<?php print($contextname);?>' :value="contextid">
 | 
			
		||||
				<b-form-select-option v-for='ctx in usedcontexts' :key='ctx.id' :value="ctx.context_id" @click='switchContext(ctx)'
 | 
			
		||||
					:active="ctx.context_id == contextid" :class="(ctx.studyplancount > 0)?'font-weight-bold':''"
 | 
			
		||||
				><span v-for="(p,i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</b-form-select-option>
 | 
			
		||||
				><span v-for="(p, i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</b-form-select-option>
 | 
			
		||||
			</b-form-select>
 | 
			
		||||
		</div>
 | 
			
		||||
		<h3 v-else><?php print $contextname; ?></h3>
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +118,7 @@ print $OUTPUT->header();
 | 
			
		|||
			<a href='#' v-if='activestudyplan' @click.prevent='closeStudyplan'><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
 | 
			
		||||
			<span v-if='activestudyplan'><?php t("studyplan_select"); ?></span> 
 | 
			
		||||
			<b-form-select v-if='activestudyplan' lazy :text='dropdown_title' v-model='activestudyplan.id'>
 | 
			
		||||
				<b-form-select-option v-for='(studyplan,planindex) in studyplans' :value="studyplan.id" :key='studyplan.id' @click='selectStudyplan(studyplan)'>{{ studyplan.name }}</b-form-select-option>
 | 
			
		||||
				<b-form-select-option v-for='(studyplan, planindex) in studyplans' :value="studyplan.id" :key='studyplan.id' @click='selectStudyplan(studyplan)'>{{ studyplan.name }}</b-form-select-option>
 | 
			
		||||
			</b-form-select> 
 | 
			
		||||
			<t-studyplan-edit
 | 
			
		||||
				@creating=""
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +143,7 @@ print $OUTPUT->header();
 | 
			
		|||
				<p><?php t("studyplan_noneselected"); ?></p>
 | 
			
		||||
				<b-card-group deck>
 | 
			
		||||
					<s-studyplan-card
 | 
			
		||||
						v-for='(studyplan,planindex) in studyplans' 
 | 
			
		||||
						v-for='(studyplan, planindex) in studyplans'
 | 
			
		||||
						:key='studyplan.id'
 | 
			
		||||
						v-model='studyplans[planindex]'
 | 
			
		||||
						open
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,34 @@
 | 
			
		|||
<?php
 | 
			
		||||
if(isset($_SERVER['SCRIPT_FILENAME']))
 | 
			
		||||
{
 | 
			
		||||
	// If SCRIPT_FILENAME is set, use that so the symlinked directories the developmen environment uses are handled correctly
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
if (isset($_SERVER['SCRIPT_FILENAME'])) {
 | 
			
		||||
	// If SCRIPT_FILENAME is set, use that so the symlinked directories the developmen environment uses are handled correctly.
 | 
			
		||||
	$root = dirname(dirname(dirname($_SERVER['SCRIPT_FILENAME'])));
 | 
			
		||||
	error_log("Using {$root}/config.php");
 | 
			
		||||
	require_once($root."/config.php");
 | 
			
		||||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
	// If not, assume the cwd is not symlinked and proceed as we are used to
 | 
			
		||||
	// If not, assume the cwd is not symlinked and proceed as we are used to.
 | 
			
		||||
	require_once("../../config.php");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,10 +39,10 @@ use local_treestudyplan;
 | 
			
		|||
 | 
			
		||||
$INVITED_URL = "/local/treestudyplan/invited.php";
 | 
			
		||||
 | 
			
		||||
//admin_externalpage_setup('major');
 | 
			
		||||
//admin_externalpage_setup('major');.
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/invitations.php",array());
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/invitations.php", array());
 | 
			
		||||
require_login();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_pagelayout('base');
 | 
			
		||||
| 
						 | 
				
			
			@ -30,16 +50,15 @@ $PAGE->set_context($systemcontext);
 | 
			
		|||
$PAGE->set_title(get_string('manage_invites', 'local_treestudyplan'));
 | 
			
		||||
$PAGE->set_heading(get_string('manage_invites', 'local_treestudyplan'));
 | 
			
		||||
 | 
			
		||||
// Load javascripts
 | 
			
		||||
// Load javascripts.
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-invitemanager', 'init');
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/buttonlinks', 'init');
 | 
			
		||||
 | 
			
		||||
// retrieve list of courses that the student is enrolled in
 | 
			
		||||
$sent = optional_param('sent','',PARAM_INT);
 | 
			
		||||
if(!empty($sent))
 | 
			
		||||
{
 | 
			
		||||
// retrieve list of courses that the student is enrolled in.
 | 
			
		||||
$sent = optional_param('sent', '', PARAM_INT);
 | 
			
		||||
if (!empty($sent)) {
 | 
			
		||||
	$invite = $DB->get_record('local_treestudyplan_invit', array('id' => $sent));
 | 
			
		||||
	\core\notification::success(get_string('invite_resent_msg','local_treestudyplan',$invite));
 | 
			
		||||
	\core\notification::success(get_string('invite_resent_msg', 'local_treestudyplan', $invite));
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,19 +68,18 @@ print "<p>".get_string('invite_description', 'local_treestudyplan')."</p>";
 | 
			
		|||
 | 
			
		||||
$invites = $DB->get_records('local_treestudyplan_invit', array('user_id' => $USER->id));
 | 
			
		||||
 | 
			
		||||
print "<h3>".get_string('invite_tablecaption','local_treestudyplan')."</h3>";
 | 
			
		||||
print "<h3>".get_string('invite_tablecaption', 'local_treestudyplan')."</h3>";
 | 
			
		||||
print "<table class='m-manage_invites'>";
 | 
			
		||||
print "<thead>";
 | 
			
		||||
print "<th>".get_string('invite_name','local_treestudyplan')."</th>";
 | 
			
		||||
print "<th>".get_string('invite_email','local_treestudyplan')."</th>";
 | 
			
		||||
print "<th>".get_string('invite_date','local_treestudyplan')."</th>";
 | 
			
		||||
print "<th>".get_string('invite_name', 'local_treestudyplan')."</th>";
 | 
			
		||||
print "<th>".get_string('invite_email', 'local_treestudyplan')."</th>";
 | 
			
		||||
print "<th>".get_string('invite_date', 'local_treestudyplan')."</th>";
 | 
			
		||||
print "<th> </th>";
 | 
			
		||||
print "</thead>";
 | 
			
		||||
 | 
			
		||||
print "<tbody>";
 | 
			
		||||
if(count($invites) > 0)
 | 
			
		||||
{
 | 
			
		||||
	foreach($invites as $invite)
 | 
			
		||||
if (count($invites) > 0) {
 | 
			
		||||
	foreach ($invites as $invite)
 | 
			
		||||
	{
 | 
			
		||||
		$testlink  = $INVITED_URL."?key={$invite->invitekey}";
 | 
			
		||||
		print "<tr data-id='{$invite->id}'>";
 | 
			
		||||
| 
						 | 
				
			
			@ -70,19 +88,19 @@ if(count($invites) > 0)
 | 
			
		|||
		print "<td data-field='date'>".userdate($invite->idate, "%x")."</td>";
 | 
			
		||||
		print "<td data-field='control'>";
 | 
			
		||||
 | 
			
		||||
		print "<a class='m-action-view ' href='{$testlink}' title='".get_string('invite_tooltip_testlink','local_treestudyplan')."'><i class='fa fa-eye'></i></a>";
 | 
			
		||||
		print "<a class='m-action-view ' href='{$testlink}' title='".get_string('invite_tooltip_testlink', 'local_treestudyplan')."'><i class='fa fa-eye'></i></a>";
 | 
			
		||||
 | 
			
		||||
		print "<a class='m-action-resend m-action-confirm'";
 | 
			
		||||
		print " data-confirmtext='".get_string('invite_confirm_resend','local_treestudyplan',$invite->name)."'";
 | 
			
		||||
		print " data-confirmbtn='".get_string('send','local_treestudyplan')."'";
 | 
			
		||||
		print " href='#' data-actionhref='edit-invite.php?resend={$invite->id}' title='".get_string('invite_tooltip_resend','local_treestudyplan')."'";
 | 
			
		||||
		print " data-confirmtext='".get_string('invite_confirm_resend', 'local_treestudyplan', $invite->name)."'";
 | 
			
		||||
		print " data-confirmbtn='".get_string('send', 'local_treestudyplan')."'";
 | 
			
		||||
		print " href='#' data-actionhref='edit-invite.php?resend={$invite->id}' title='".get_string('invite_tooltip_resend', 'local_treestudyplan')."'";
 | 
			
		||||
		print " ><i class='fa fa-envelope'></i></a>";
 | 
			
		||||
 | 
			
		||||
		print "<a href='edit-invite.php?update={$invite->id}'><i class='fa fa-pencil' title='".get_string('invite_tooltip_edit','local_treestudyplan')."'></i></a>";
 | 
			
		||||
		print "<a href='edit-invite.php?update={$invite->id}'><i class='fa fa-pencil' title='".get_string('invite_tooltip_edit', 'local_treestudyplan')."'></i></a>";
 | 
			
		||||
		print "<a class='m-action-delete m-action-confirm'";
 | 
			
		||||
		print " data-confirmtext='".get_string('invite_confirm_delete','local_treestudyplan', $invite->name)."'";
 | 
			
		||||
		print " data-confirmtext='".get_string('invite_confirm_delete', 'local_treestudyplan', $invite->name)."'";
 | 
			
		||||
		print " data-confirmbtn='".get_string('delete')."'";
 | 
			
		||||
		print " href='#' data-actionhref='edit-invite.php?delete={$invite->id}' title='".get_string('invite_tooltip_delete','local_treestudyplan')."'";
 | 
			
		||||
		print " href='#' data-actionhref='edit-invite.php?delete={$invite->id}' title='".get_string('invite_tooltip_delete', 'local_treestudyplan')."'";
 | 
			
		||||
    	print " ><i class='fa fa-trash'></i></a>";
 | 
			
		||||
 | 
			
		||||
		print "</td>";
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +108,7 @@ if(count($invites) > 0)
 | 
			
		|||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
	print "<tr><td colspan='6'>".get_string('invite_table_empty','local_treestudyplan')."</td></tr>";
 | 
			
		||||
	print "<tr><td colspan='6'>".get_string('invite_table_empty', 'local_treestudyplan')."</td></tr>";
 | 
			
		||||
}
 | 
			
		||||
print "</tbody></table>";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										45
									
								
								invited.php
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								invited.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,24 +1,43 @@
 | 
			
		|||
<?php
 | 
			
		||||
// If not, assume the cwd is not symlinked and proceed as we are used to
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
//Local translate function
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan'){
 | 
			
		||||
	print get_string($str,$plugin,$param);
 | 
			
		||||
//Local translate function.
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan') {
 | 
			
		||||
	print get_string($str, $plugin, $param);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
$PAGE->set_pagelayout('base');
 | 
			
		||||
$PAGE->set_context($systemcontext);
 | 
			
		||||
 | 
			
		||||
// See if we can get a valid user for this invited
 | 
			
		||||
$invitekey = optional_param('key', '', PARAM_ALPHANUM);     // module name
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/invited.php",array('key' => $invitekey));
 | 
			
		||||
// See if we can get a valid user for this invited.
 | 
			
		||||
$invitekey = optional_param('key', '', PARAM_ALPHANUM);     // module name.
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/invited.php", array('key' => $invitekey));
 | 
			
		||||
 | 
			
		||||
$invite = $DB->get_record_select("local_treestudyplan_invit", $DB->sql_compare_text("invitekey"). " = " . $DB->sql_compare_text(":invitekey"), ['invitekey' => $invitekey]);
 | 
			
		||||
 | 
			
		||||
if(empty($invite))
 | 
			
		||||
{
 | 
			
		||||
if (empty($invite)) {
 | 
			
		||||
	$PAGE->set_title(get_string('invalid_invitekey_title', 'local_treestudyplan'));
 | 
			
		||||
	$PAGE->set_heading(get_string('invalid_invitekey_title', 'local_treestudyplan'));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -26,10 +45,10 @@ if(empty($invite))
 | 
			
		|||
	print $OUTPUT->header();
 | 
			
		||||
	
 | 
			
		||||
	
 | 
			
		||||
	// render page for skill level 0 (global)
 | 
			
		||||
	// render page for skill level 0 (global).
 | 
			
		||||
 | 
			
		||||
	print "<div class='box errorbox alert alert-danger'>";
 | 
			
		||||
	print get_string('invalid_invitekey_error','local_treestudyplan');
 | 
			
		||||
	print get_string('invalid_invitekey_error', 'local_treestudyplan');
 | 
			
		||||
	print "</div>";
 | 
			
		||||
 | 
			
		||||
	print $OUTPUT->footer();
 | 
			
		||||
| 
						 | 
				
			
			@ -38,10 +57,10 @@ if(empty($invite))
 | 
			
		|||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
	// Load javascripts and specific css
 | 
			
		||||
	// Load javascripts and specific css.
 | 
			
		||||
	$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
 | 
			
		||||
	$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
 | 
			
		||||
	$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init',['invited',$invitekey]);
 | 
			
		||||
	$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init', ['invited', $invitekey]);
 | 
			
		||||
 | 
			
		||||
	$student = $DB->get_record('user', array('id' => $invite->user_id));
 | 
			
		||||
	$PAGE->set_title(get_string('report_invited', 'local_treestudyplan', "{$student->firstname} {$student->lastname}" ));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
$string['pluginname'] = 'Studyplans';
 | 
			
		||||
 | 
			
		||||
$string['privacy:metadata'] = 'This plugin stores a link between users and their associated study plans. It also needs to store an email address and user provided handle, which may be a name, to send invitations to view the study plan to whomever the user chooses by email.';
 | 
			
		||||
| 
						 | 
				
			
			@ -45,13 +66,13 @@ $string['invite_date'] = "Date";
 | 
			
		|||
$string['invite_resent_msg'] = 'The invitation for {$a->name}<{$a->email}> has been sent';
 | 
			
		||||
$string['invite_mail_subject'] = 'Shared grade card of {$a->sender}';
 | 
			
		||||
$string['invite_mail_text'] = '
 | 
			
		||||
<p>Dear {$a->invitee},</p>
 | 
			
		||||
<p>Dear {$a->invitee}, </p>
 | 
			
		||||
<p>I\'d like to invite you to view my study plan and progess.</p>
 | 
			
		||||
<p>The link below gives you access at any time to view the most recent results. Feel free to bookmark this link in your browser.</p>
 | 
			
		||||
 | 
			
		||||
<p>Click the link below to view the study plan:<br>
 | 
			
		||||
<a href="{$a->link}">{$a->link}</a></p>
 | 
			
		||||
<p>Kind regards,<br>
 | 
			
		||||
<p>Kind regards, <br>
 | 
			
		||||
{$a->sender}</p>
 | 
			
		||||
';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,24 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
$string['pluginname'] = 'Studieplannen';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +66,7 @@ $string['invite_date'] = "Datum";
 | 
			
		|||
$string['invite_resent_msg'] = 'De uitnodiging naar {$a->name}<{$a->email}> is verzonden';
 | 
			
		||||
$string['invite_mail_subject'] = 'Gedeeld rapport van {$a->sender}';
 | 
			
		||||
$string['invite_mail_text'] = '
 | 
			
		||||
<p>Beste {$a->invitee},</p>
 | 
			
		||||
<p>Beste {$a->invitee}, </p>
 | 
			
		||||
<p>Bij deze wil ik je graag uitnodigen om mijn studieplan en studievoortgang te bekijken.</p>
 | 
			
		||||
 | 
			
		||||
<p>Via de link hieronder kun je op elk moment het meest recente resultatenoverzicht bekijken. Je kunt deze link ook bewaren als bookmark in je browser.</p>
 | 
			
		||||
| 
						 | 
				
			
			@ -54,7 +74,7 @@ $string['invite_mail_text'] = '
 | 
			
		|||
<p>Klik op de volgende link om het studieplan te bekijken:<br>
 | 
			
		||||
<a href="{$a->link}">{$a->link}</a></p>
 | 
			
		||||
 | 
			
		||||
<p>Met vriendelijke groet,<br>
 | 
			
		||||
<p>Met vriendelijke groet, <br>
 | 
			
		||||
{$a->sender}</p>
 | 
			
		||||
';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										153
									
								
								lib.php
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								lib.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once($CFG->dirroot.'/course/modlib.php');
 | 
			
		||||
 | 
			
		||||
use local_treestudyplan\local\helpers\webservicehelper;
 | 
			
		||||
| 
						 | 
				
			
			@ -15,32 +36,32 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
 | 
			
		||||
	$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
	// Moodle 4.0-4.2 do not yet support customizing the primary navigation bar (it is a planned feature though)
 | 
			
		||||
	// For now, go to theme settings and add the following into "Custom menu items"
 | 
			
		||||
	//    [your name for my studyplan]|/local/treestudyplan/myreport.php
 | 
			
		||||
	//    [your name for studyplan viewing]|/local/treestudyplan/view-plan.php
 | 
			
		||||
	//    [your name for studyplan managing]|/local/treestudyplan/edit-plan.php
 | 
			
		||||
	// For example:
 | 
			
		||||
	//    Mijn studieplan|/local/treestudyplan/myreport.php
 | 
			
		||||
	//    Studieplannen|/local/treestudyplan/view-plan.php
 | 
			
		||||
	//    Studieplannen beheren|/local/treestudyplan/edit-plan.php
 | 
			
		||||
	// Moodle 4.0-4.2 do not yet support customizing the primary navigation bar (it is a planned feature though).
 | 
			
		||||
	// For now, go to theme settings and add the following into "Custom menu items".
 | 
			
		||||
	//    [your name for my studyplan]|/local/treestudyplan/myreport.php.
 | 
			
		||||
	//    [your name for studyplan viewing]|/local/treestudyplan/view-plan.php.
 | 
			
		||||
	//    [your name for studyplan managing]|/local/treestudyplan/edit-plan.php.
 | 
			
		||||
	// For example:.
 | 
			
		||||
	//    Mijn studieplan|/local/treestudyplan/myreport.php.
 | 
			
		||||
	//    Studieplannen|/local/treestudyplan/view-plan.php.
 | 
			
		||||
	//    Studieplannen beheren|/local/treestudyplan/edit-plan.php.
 | 
			
		||||
 | 
			
		||||
	// Using some javascript magic we'll hide the links that are not accessible
 | 
			
		||||
	// (Since the Output API does not easily support inline style tags, adding one through Javascript is easier,
 | 
			
		||||
	//  and not much more complex than loading a separate stylesheet for each link we want to hide)
 | 
			
		||||
	// we will add all the hrefs that should be hidden to this variable below
 | 
			
		||||
	// Using some javascript magic we'll hide the links that are not accessible.
 | 
			
		||||
	// (Since the Output API does not easily support inline style tags, adding one through Javascript is easier,.
 | 
			
		||||
	//  and not much more complex than loading a separate stylesheet for each link we want to hide).
 | 
			
		||||
	// we will add all the hrefs that should be hidden to this variable below.
 | 
			
		||||
	$hide_primary_hrefs = [];
 | 
			
		||||
 | 
			
		||||
	if($USER->id > 1) // Don't show if user is not logged in (id == 0) or is guest user (id == 1)
 | 
			
		||||
	if ($USER->id > 1) // Don't show if user is not logged in (id == 0) or is guest user (id == 1).
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
		$userstudyplans = studyplan::find_for_user($USER->id);
 | 
			
		||||
		if(!empty($userstudyplans))
 | 
			
		||||
		if (!empty($userstudyplans))
 | 
			
		||||
		{
 | 
			
		||||
		
 | 
			
		||||
			// create studyplan node
 | 
			
		||||
			// create studyplan node.
 | 
			
		||||
			$node = navigation_node::create(
 | 
			
		||||
				get_string("link_myreport","local_treestudyplan"),
 | 
			
		||||
				get_string("link_myreport", "local_treestudyplan"),
 | 
			
		||||
				new moodle_url($CFG->wwwroot . "/local/treestudyplan/myreport.php", array()),
 | 
			
		||||
				global_navigation::TYPE_SYSTEM,
 | 
			
		||||
				null,
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +71,9 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
			$node->showinflatnavigation = true;
 | 
			
		||||
			$node->showinsecondarynavigation=true;
 | 
			
		||||
		
 | 
			
		||||
			// create invitenode node
 | 
			
		||||
			// create invitenode node.
 | 
			
		||||
			$invitenode = navigation_node::create(
 | 
			
		||||
				get_string("manage_invites","local_treestudyplan"),
 | 
			
		||||
				get_string("manage_invites", "local_treestudyplan"),
 | 
			
		||||
				new moodle_url($CFG->wwwroot . "/local/treestudyplan/invitations.php", array()),
 | 
			
		||||
				global_navigation::TYPE_CUSTOM ,
 | 
			
		||||
				null,
 | 
			
		||||
| 
						 | 
				
			
			@ -63,16 +84,16 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
			$node->add_node($invitenode);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			$navigation->add_node($node,'mycourses');
 | 
			
		||||
			$navigation->add_node($node, 'mycourses');
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			$hide_primary_hrefs[] = "/local/treestudyplan/myreport.php";
 | 
			
		||||
		}
 | 
			
		||||
		if(	has_capability('local/treestudyplan:viewuserreports',context_system::instance()) 
 | 
			
		||||
		if (	has_capability('local/treestudyplan:viewuserreports', context_system::instance())
 | 
			
		||||
			|| webservicehelper::has_capability_in_any_category('local/treestudyplan:viewuserreports'))
 | 
			
		||||
		{
 | 
			
		||||
			$node = navigation_node::create(
 | 
			
		||||
				get_string("link_viewplan","local_treestudyplan"),
 | 
			
		||||
				get_string("link_viewplan", "local_treestudyplan"),
 | 
			
		||||
				new moodle_url($CFG->wwwroot . "/local/treestudyplan/view-plan.php", array()),
 | 
			
		||||
				global_navigation::TYPE_SYSTEM ,
 | 
			
		||||
				null,
 | 
			
		||||
| 
						 | 
				
			
			@ -81,17 +102,17 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
				);
 | 
			
		||||
			$node->showinflatnavigation = true;
 | 
			
		||||
			$node->showinsecondarynavigation=true;
 | 
			
		||||
			$navigation->add_node($node,'mycourses');
 | 
			
		||||
			$navigation->add_node($node, 'mycourses');
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			$hide_primary_hrefs[] = "/local/treestudyplan/view-plan.php";
 | 
			
		||||
		}
 | 
			
		||||
		if(	has_capability('local/treestudyplan:editstudyplan',context_system::instance())
 | 
			
		||||
		if (	has_capability('local/treestudyplan:editstudyplan', context_system::instance())
 | 
			
		||||
		  	|| webservicehelper::has_capability_in_any_category('local/treestudyplan:editstudyplan')
 | 
			
		||||
		   )
 | 
			
		||||
		{
 | 
			
		||||
			$node = navigation_node::create(
 | 
			
		||||
				get_string("cfg_plans","local_treestudyplan"),
 | 
			
		||||
				get_string("cfg_plans", "local_treestudyplan"),
 | 
			
		||||
				new moodle_url($CFG->wwwroot . "/local/treestudyplan/edit-plan.php", array()),
 | 
			
		||||
				global_navigation::TYPE_SYSTEM ,
 | 
			
		||||
				null,
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +121,7 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
				);
 | 
			
		||||
			$node->showinflatnavigation = true;
 | 
			
		||||
			$node->showinsecondarynavigation=true;
 | 
			
		||||
			$navigation->add_node($node,'mycourses');
 | 
			
		||||
			$navigation->add_node($node, 'mycourses');
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			$hide_primary_hrefs[] = "/local/treestudyplan/edit-plan.php";
 | 
			
		||||
| 
						 | 
				
			
			@ -111,9 +132,9 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
		$hide_primary_hrefs[] = "/local/treestudyplan/edit-plan.php";
 | 
			
		||||
		$hide_primary_hrefs[] = "/local/treestudyplan/view-plan.php";
 | 
			
		||||
	}
 | 
			
		||||
	// create invitenode node
 | 
			
		||||
	// create invitenode node.
 | 
			
		||||
	$invitenode = navigation_node::create(
 | 
			
		||||
		get_string("nav_invited","local_treestudyplan"),
 | 
			
		||||
		get_string("nav_invited", "local_treestudyplan"),
 | 
			
		||||
		new moodle_url($CFG->wwwroot . "/local/treestudyplan/invited.php", array()),
 | 
			
		||||
		global_navigation::TYPE_USER ,
 | 
			
		||||
		null,
 | 
			
		||||
| 
						 | 
				
			
			@ -121,10 +142,10 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
		new pix_icon("nav_invited", '', 'local_treestudyplan')
 | 
			
		||||
		);
 | 
			
		||||
	$invitenode->showinflatnavigation = false;
 | 
			
		||||
	$navigation->add_node($invitenode,'mycourses');
 | 
			
		||||
	$navigation->add_node($invitenode, 'mycourses');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// Now using some javascript magic, we'll hide the links that are not accessible
 | 
			
		||||
	// Now using some javascript magic, we'll hide the links that are not accessible.
 | 
			
		||||
	$PAGE->requires->js_call_amd('local_treestudyplan/primary-nav-tools', 'hide_primary', [$hide_primary_hrefs]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -134,27 +155,27 @@ function local_treestudyplan_extend_navigation(global_navigation $navigation) {
 | 
			
		|||
function local_treestudyplan_extend_navigation_category_settings($navigation, context_coursecat $coursecategorycontext) {
 | 
			
		||||
	global $CFG, $PAGE;
 | 
			
		||||
	$categoryid = $coursecategorycontext->instanceid;
 | 
			
		||||
	if(has_capability('local/treestudyplan:editstudyplan',$coursecategorycontext)){
 | 
			
		||||
	if (has_capability('local/treestudyplan:editstudyplan', $coursecategorycontext)) {
 | 
			
		||||
		$node = $navigation->add(
 | 
			
		||||
			get_string('treestudyplan:editstudyplan',"local_treestudyplan"),
 | 
			
		||||
			get_string('treestudyplan:editstudyplan', "local_treestudyplan"),
 | 
			
		||||
			new moodle_url($CFG->wwwroot . "/local/treestudyplan/edit-plan.php", ["categoryid"=>$categoryid]),
 | 
			
		||||
			global_navigation::TYPE_CATEGORY,
 | 
			
		||||
			null,
 | 
			
		||||
			"local_treestudyplan_editplan",
 | 
			
		||||
			new pix_icon("editplans", '', 'local_treestudyplan')
 | 
			
		||||
		);
 | 
			
		||||
		//$node->make_active();
 | 
			
		||||
		//$node->make_active();.
 | 
			
		||||
	}
 | 
			
		||||
	if(has_capability('local/treestudyplan:viewuserreports',$coursecategorycontext)){
 | 
			
		||||
	if (has_capability('local/treestudyplan:viewuserreports', $coursecategorycontext)) {
 | 
			
		||||
		$node = $navigation->add(
 | 
			
		||||
			get_string('link_viewplan',"local_treestudyplan"),
 | 
			
		||||
			get_string('link_viewplan', "local_treestudyplan"),
 | 
			
		||||
			new moodle_url($CFG->wwwroot . "/local/treestudyplan/view-plan.php", ["categoryid"=>$categoryid]),
 | 
			
		||||
			global_navigation::TYPE_CATEGORY,
 | 
			
		||||
			null,
 | 
			
		||||
			"local_treestudyplan_viewplan",
 | 
			
		||||
			new pix_icon("viewplans", '', 'local_treestudyplan')
 | 
			
		||||
		);
 | 
			
		||||
		//$node->make_active();
 | 
			
		||||
		//$node->make_active();.
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -184,16 +205,15 @@ function local_treestudyplan_reset_fontawesome_icon_map() {
 | 
			
		|||
    $instance->get_icon_name_map();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function local_treestudyplan_send_invite($inviteid)
 | 
			
		||||
{
 | 
			
		||||
	global $DB,$USER,$CFG;
 | 
			
		||||
function local_treestudyplan_send_invite($inviteid) {
 | 
			
		||||
	global $DB, $USER, $CFG;
 | 
			
		||||
	$invite = $DB->get_record("local_treestudyplan_invit", array('id' => $inviteid));
 | 
			
		||||
 | 
			
		||||
	$noreply = 'noreply@' . get_host_from_url($CFG->wwwroot);
 | 
			
		||||
	$mailer = get_mailer();
 | 
			
		||||
	$mailer->setFrom($noreply,"{$USER->firstname} {$USER->lastname}");
 | 
			
		||||
	$mailer->addAddress($invite->email,$invite->name);
 | 
			
		||||
	$mailer->addReplyTo($USER->email,"{$USER->firstname} {$USER->lastname}");
 | 
			
		||||
	$mailer->setFrom($noreply, "{$USER->firstname} {$USER->lastname}");
 | 
			
		||||
	$mailer->addAddress($invite->email, $invite->name);
 | 
			
		||||
	$mailer->addReplyTo($USER->email, "{$USER->firstname} {$USER->lastname}");
 | 
			
		||||
 | 
			
		||||
	$invitehref = $CFG->wwwroot."/local/treestudyplan/invited.php?key={$invite->invitekey}";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -202,32 +222,32 @@ function local_treestudyplan_send_invite($inviteid)
 | 
			
		|||
				'sender' => "{$USER->firstname} {$USER->lastname}",
 | 
			
		||||
				'link' => $invitehref];
 | 
			
		||||
 | 
			
		||||
	if($invite->allow_details || $invite->allow_calendar || $invite->allow_badges)
 | 
			
		||||
	if ($invite->allow_details || $invite->allow_calendar || $invite->allow_badges)
 | 
			
		||||
	{
 | 
			
		||||
		$data['permissions'] = get_string('invite_mail_permissions','local_treestudyplan');
 | 
			
		||||
		$data['permissions'] = get_string('invite_mail_permissions', 'local_treestudyplan');
 | 
			
		||||
		$data['permissions'] .= "<ul>\n";
 | 
			
		||||
		if($invite->allow_details )
 | 
			
		||||
		if ($invite->allow_details )
 | 
			
		||||
		{
 | 
			
		||||
			$data['permissions'] .= "<li>".get_string('invite_allow_details','local_treestudyplan')."</li>\n";
 | 
			
		||||
			$data['permissions'] .= "<li>".get_string('invite_allow_details', 'local_treestudyplan')."</li>\n";
 | 
			
		||||
		}
 | 
			
		||||
		if($invite->allow_calendar)
 | 
			
		||||
		if ($invite->allow_calendar)
 | 
			
		||||
		{
 | 
			
		||||
			$data['permissions'] .= "<li>".get_string('invite_allow_calendar','local_treestudyplan')."</li>\n";
 | 
			
		||||
			$data['permissions'] .= "<li>".get_string('invite_allow_calendar', 'local_treestudyplan')."</li>\n";
 | 
			
		||||
		}
 | 
			
		||||
		if($invite->allow_badges)
 | 
			
		||||
		if ($invite->allow_badges)
 | 
			
		||||
		{
 | 
			
		||||
			$data['permissions'] .= "<li>".get_string('invite_allow_badges','local_treestudyplan')."</li>\n";
 | 
			
		||||
			$data['permissions'] .= "<li>".get_string('invite_allow_badges', 'local_treestudyplan')."</li>\n";
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		$data['permissions'] .= "</ul></p>\n";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$body = get_string('invite_mail_text','local_treestudyplan',$data);
 | 
			
		||||
	$subject = get_string('invite_mail_subject','local_treestudyplan', $data);
 | 
			
		||||
	$body = get_string('invite_mail_text', 'local_treestudyplan', $data);
 | 
			
		||||
	$subject = get_string('invite_mail_subject', 'local_treestudyplan', $data);
 | 
			
		||||
 | 
			
		||||
	$html = "
 | 
			
		||||
	<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>
 | 
			
		||||
	<html xmlns='http://www.w3.org/1999/xhtml'>
 | 
			
		||||
	<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>.
 | 
			
		||||
	<html xmlns='http://www.w3.org/1999/xhtml'>.
 | 
			
		||||
	 <head>
 | 
			
		||||
	  <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
 | 
			
		||||
	  <title>{$subject}</title>
 | 
			
		||||
| 
						 | 
				
			
			@ -247,8 +267,7 @@ function local_treestudyplan_send_invite($inviteid)
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function local_treestudyplan_find_cohortmembers($cohortid)
 | 
			
		||||
{
 | 
			
		||||
function local_treestudyplan_find_cohortmembers($cohortid) {
 | 
			
		||||
    global $DB;
 | 
			
		||||
    // By default wherecondition retrieves all users except the deleted, not confirmed and guest.
 | 
			
		||||
    $params = ['cohortid' => $cohortid];
 | 
			
		||||
| 
						 | 
				
			
			@ -257,24 +276,22 @@ function local_treestudyplan_find_cohortmembers($cohortid)
 | 
			
		|||
            WHERE u.suspended = 0 AND u.id > 1
 | 
			
		||||
            ORDER BY u.lastname
 | 
			
		||||
            ";
 | 
			
		||||
    $availableusers = $DB->get_records_sql($sql,$params);
 | 
			
		||||
    $availableusers = $DB->get_records_sql($sql, $params);
 | 
			
		||||
    return $availableusers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function local_treestudyplan_get_cohort_path($cohort)
 | 
			
		||||
{
 | 
			
		||||
function local_treestudyplan_get_cohort_path($cohort) {
 | 
			
		||||
    $cohortcontext = context::instance_by_id($cohort->contextid);
 | 
			
		||||
    if($cohortcontext && $cohortcontext->id != SYSCONTEXTID)
 | 
			
		||||
    {
 | 
			
		||||
    if ($cohortcontext && $cohortcontext->id != SYSCONTEXTID) {
 | 
			
		||||
        $ctxpath = array_map(
 | 
			
		||||
            function($ctx){ return $ctx->get_context_name(false);},
 | 
			
		||||
            function($ctx) { return $ctx->get_context_name(false);},
 | 
			
		||||
            $cohortcontext->get_parent_contexts(true)
 | 
			
		||||
            );
 | 
			
		||||
        array_pop($ctxpath); // pop system context off the list
 | 
			
		||||
        array_pop($ctxpath); // pop system context off the list.
 | 
			
		||||
        $ctxpath = array_reverse($ctxpath);
 | 
			
		||||
        $ctxpath[] = $cohort->name;
 | 
			
		||||
 | 
			
		||||
        return implode(" / ",$ctxpath);
 | 
			
		||||
        return implode(" / ", $ctxpath);
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -283,13 +300,13 @@ function local_treestudyplan_get_cohort_path($cohort)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function local_treestudyplan_output_fragment_mod_edit_form($args){
 | 
			
		||||
function local_treestudyplan_output_fragment_mod_edit_form($args) {
 | 
			
		||||
	global $CFG;
 | 
			
		||||
	global $DB;
 | 
			
		||||
	$args = (object)$args;
 | 
			
		||||
	$context = $args->context;
 | 
			
		||||
	
 | 
			
		||||
	if(empty($args->cmid)){
 | 
			
		||||
	if (empty($args->cmid)) {
 | 
			
		||||
		return "RANDOM!";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -299,8 +316,8 @@ function local_treestudyplan_output_fragment_mod_edit_form($args){
 | 
			
		|||
	// Check the course exists.
 | 
			
		||||
	$course = \get_course($cm->course);
 | 
			
		||||
 | 
			
		||||
	// require_login
 | 
			
		||||
	require_login($course, false, $cm); // needed to setup proper $COURSE
 | 
			
		||||
	// require_login.
 | 
			
		||||
	require_login($course, false, $cm); // needed to setup proper $COURSE.
 | 
			
		||||
 | 
			
		||||
	list($cm, $context, $module, $data, $cw) = \get_moduleinfo_data($cm, $course);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/weblib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -7,22 +28,22 @@ use local_treestudyplan;
 | 
			
		|||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/myreport.php",array());
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/myreport.php", array());
 | 
			
		||||
require_login();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_pagelayout('embedded');
 | 
			
		||||
$PAGE->set_context($systemcontext);
 | 
			
		||||
$PAGE->set_title(get_string('report_invited','local_treestudyplan',"{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
$PAGE->set_heading(get_string('report_invited','local_treestudyplan',"{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
$PAGE->set_title(get_string('report_invited', 'local_treestudyplan', "{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
$PAGE->set_heading(get_string('report_invited', 'local_treestudyplan', "{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
 | 
			
		||||
// Load javascripts and specific css
 | 
			
		||||
// Load javascripts and specific css.
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init');
 | 
			
		||||
 | 
			
		||||
//Local translate function
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan'){
 | 
			
		||||
	print get_string($str,$plugin,$param);
 | 
			
		||||
//Local translate function.
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan') {
 | 
			
		||||
	print get_string($str, $plugin, $param);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->header();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								myreport.php
									
									
									
									
									
								
							
							
						
						
									
										47
									
								
								myreport.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/weblib.php');
 | 
			
		||||
| 
						 | 
				
			
			@ -7,36 +28,36 @@ use local_treestudyplan;
 | 
			
		|||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/myreport.php",array());
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/myreport.php", array());
 | 
			
		||||
require_login();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_pagelayout('base');
 | 
			
		||||
$PAGE->set_context($systemcontext);
 | 
			
		||||
 | 
			
		||||
$teachermode = has_capability("local/treestudyplan:viewuserreports",$systemcontext);
 | 
			
		||||
$teachermode = has_capability("local/treestudyplan:viewuserreports", $systemcontext);
 | 
			
		||||
 | 
			
		||||
if($teachermode){
 | 
			
		||||
	$PAGE->set_title(get_string('myreport_teachermode','local_treestudyplan'));
 | 
			
		||||
	$PAGE->set_heading(get_string('myreport_teachermode','local_treestudyplan'));
 | 
			
		||||
if ($teachermode) {
 | 
			
		||||
	$PAGE->set_title(get_string('myreport_teachermode', 'local_treestudyplan'));
 | 
			
		||||
	$PAGE->set_heading(get_string('myreport_teachermode', 'local_treestudyplan'));
 | 
			
		||||
} else {
 | 
			
		||||
	$PAGE->set_title(get_string('report_invited','local_treestudyplan',"{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
	$PAGE->set_heading(get_string('report_invited','local_treestudyplan',"{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
	$PAGE->set_title(get_string('report_invited', 'local_treestudyplan', "{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
	$PAGE->set_heading(get_string('report_invited', 'local_treestudyplan', "{$USER->firstname} {$USER->lastname}"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load javascripts and specific css
 | 
			
		||||
// Load javascripts and specific css.
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init',[$teachermode?'teaching':'myreport']);
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init', [$teachermode?'teaching':'myreport']);
 | 
			
		||||
 | 
			
		||||
//Local translate function
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan'){
 | 
			
		||||
	print get_string($str,$plugin,$param);
 | 
			
		||||
//Local translate function.
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan') {
 | 
			
		||||
	print get_string($str, $plugin, $param);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->header();
 | 
			
		||||
?>
 | 
			
		||||
<div class="m-buttonbar" style="margin-bottom: 1em; text-align: right;">
 | 
			
		||||
<?php if(!$teachermode){ ?>
 | 
			
		||||
<?php if (!$teachermode) { ?>
 | 
			
		||||
<a class="btn btn-primary" href="invitations.php" id="manage_invites"><i class="fa fa-share"></i> <?php t('manage_invites'); ?></a>
 | 
			
		||||
<?php } ?>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										168
									
								
								reports.php
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								reports.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,168 +0,0 @@
 | 
			
		|||
<?php
 | 
			
		||||
// If not, assume the cwd is not symlinked and proceed as we are used to
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
require_once($CFG->libdir.'/weblib.php');
 | 
			
		||||
require_once($CFG->dirroot.'/grade/querylib.php');
 | 
			
		||||
require_once($CFG->dirroot.'/cohort/lib.php');
 | 
			
		||||
require_once($CFG->dirroot.'/cohort/locallib.php');
 | 
			
		||||
require_once("lib.php");
 | 
			
		||||
 | 
			
		||||
use local_treestudyplan;
 | 
			
		||||
 | 
			
		||||
//admin_externalpage_setup('major');
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
require_login();
 | 
			
		||||
require_capability('local/treestudyplan:viewothercards',$systemcontext);
 | 
			
		||||
 | 
			
		||||
$PAGE->set_pagelayout('base');
 | 
			
		||||
$PAGE->set_context($systemcontext);
 | 
			
		||||
$PAGE->set_title(get_string('report_index','local_treestudyplan'));
 | 
			
		||||
$PAGE->set_heading(get_string('report_index','local_treestudyplan'));
 | 
			
		||||
 | 
			
		||||
// Load javascripts
 | 
			
		||||
//$PAGE->requires->js_call_amd('local_treestudyplan/fixlinks', 'init', ['div.tab-content']);
 | 
			
		||||
$PAGE->requires->js_call_amd('block_gradelevel/renderbadge', 'init');
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/report', 'init');
 | 
			
		||||
//$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/eqstyles.css'));
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-toggle.min.css'));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
$userid = null;
 | 
			
		||||
$studentid = optional_param('studentid', '', PARAM_INT);
 | 
			
		||||
$cohortid = optional_param('cohortid', '', PARAM_INT);
 | 
			
		||||
if(!empty($studentid)){
 | 
			
		||||
    $PAGE->set_url("/local/treestudyplan/reports.php",array('studentid' => $studentid));
 | 
			
		||||
    $student = $DB->get_record("user",['id' => $studentid]);
 | 
			
		||||
 | 
			
		||||
    $userlist = [];
 | 
			
		||||
    $nextstudent = null;
 | 
			
		||||
    $prevstudent = null;
 | 
			
		||||
    if(!empty($cohortid))
 | 
			
		||||
    {
 | 
			
		||||
        $cohort = $DB->get_record("cohort",['id' => $cohortid]);
 | 
			
		||||
        $userlist = array_values(local_treestudyplan_find_cohortmembers($cohortid));
 | 
			
		||||
        for($i = 0; $i < count($userlist); $i++)
 | 
			
		||||
        {
 | 
			
		||||
            if($userlist[$i]->userid == $studentid)
 | 
			
		||||
            {
 | 
			
		||||
                if($i > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    $prevstudent = (object)['id' => $userlist[$i - 1]->userid, 'name' => "{$userlist[$i - 1]->firstname} {$userlist[$i - 1]->lastname}"];
 | 
			
		||||
                }
 | 
			
		||||
                if($i < count($userlist) - 1)
 | 
			
		||||
                {
 | 
			
		||||
                    $nextstudent = (object)['id' => $userlist[$i + 1]->userid, 'name' => "{$userlist[$i + 1]->firstname} {$userlist[$i + 1]->lastname}"];
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $cohortpath = local_treestudyplan_get_cohort_path($cohort);
 | 
			
		||||
        $PAGE->set_title(get_string('report_invited','local_treestudyplan',"{$cohortpath}: {$student->firstname} {$student->lastname}"));
 | 
			
		||||
        $PAGE->set_heading(get_string('report_invited','local_treestudyplan',"{$cohortpath}: {$student->firstname} {$student->lastname}"));
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        $PAGE->set_title(get_string('report_invited','local_treestudyplan',"$cohort->{$student->firstname} {$student->lastname}"));
 | 
			
		||||
        $PAGE->set_heading(get_string('report_invited','local_treestudyplan',"{$student->firstname} {$student->lastname}"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $gradewriter = new local_treestudyplan_cardwriter($studentid,true);
 | 
			
		||||
    $badgewriter = new local_treestudyplan_badgewriter($studentid);
 | 
			
		||||
    $calendarwriter = new local_treestudyplan_calendarwriter($studentid);
 | 
			
		||||
 | 
			
		||||
    print $OUTPUT->header();
 | 
			
		||||
 | 
			
		||||
    print "<div class='m-buttonbar' style='margin-bottom: 1.5em; height: 1em;'>";
 | 
			
		||||
    if(isset($prevstudent))
 | 
			
		||||
    {
 | 
			
		||||
        print "<a style='float:left;' href='/local/treestudyplan/reports.php?studentid={$prevstudent->id}&cohortid={$cohortid}'><i class='fa fa-arrow-left'></i> {$prevstudent->name} </a>";
 | 
			
		||||
    }
 | 
			
		||||
    if(isset($nextstudent))
 | 
			
		||||
    {
 | 
			
		||||
        print "<a style='float:right;' href='/local/treestudyplan/reports.php?studentid={$nextstudent->id}&cohortid={$cohortid}'>{$nextstudent->name} <i class='fa fa-arrow-right'></i></a>";
 | 
			
		||||
    }    
 | 
			
		||||
    print "</div>";
 | 
			
		||||
    print "<ul class='nav nav-tabs' role='tablist'>";
 | 
			
		||||
    print "<li class='nav-item'><a class='nav-link active' href='#link-report' data-toggle='tab' role='tab'>".get_string('nav_report','local_treestudyplan')."</a></li>";
 | 
			
		||||
    print "<li class='nav-item'><a class='nav-link ' href='#link-badges' data-toggle='tab' role='tab'>".get_string('nav_badges','local_treestudyplan')."</a></li>";
 | 
			
		||||
    print "<li class='nav-item'><a class='nav-link ' href='#link-calendar' data-toggle='tab' role='tab'>".get_string('nav_calendar','local_treestudyplan')."</a></li>";
 | 
			
		||||
    print "</ul>";
 | 
			
		||||
 | 
			
		||||
    print "<div class='tab-content mt-3'>";
 | 
			
		||||
 | 
			
		||||
    print "<div class='tab-pane active' id='link-report' data-toggle='tab' role='tab'>";
 | 
			
		||||
    print $gradewriter->render(true,false);
 | 
			
		||||
    print "</div>";
 | 
			
		||||
 | 
			
		||||
    print "<div class='tab-pane ' id='link-badges' data-toggle='tab' role='tab'>";
 | 
			
		||||
    print $badgewriter->render();
 | 
			
		||||
    print "</div>";
 | 
			
		||||
 | 
			
		||||
    print "<div class='tab-pane' id='link-calendar' data-toggle='tab' role='tab'>";
 | 
			
		||||
    print $calendarwriter->render();
 | 
			
		||||
    print "</div>";
 | 
			
		||||
 | 
			
		||||
    print "</div>";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    print $OUTPUT->footer();
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
else {
 | 
			
		||||
	$PAGE->set_url("/local/treestudyplan/reports.php",array());
 | 
			
		||||
    // show student picker
 | 
			
		||||
    $cohortlist = $DB->get_records("cohort");
 | 
			
		||||
    $cohorts = [];
 | 
			
		||||
    foreach($cohortlist as $c)
 | 
			
		||||
    {
 | 
			
		||||
	    if($c->visible)
 | 
			
		||||
	    {
 | 
			
		||||
            $cohortcontext = context::instance_by_id($c->contextid);
 | 
			
		||||
            if($cohortcontext) // TODO: add check if user has rights in this context
 | 
			
		||||
            {
 | 
			
		||||
                $users = local_treestudyplan_find_cohortmembers($c->id);
 | 
			
		||||
 | 
			
		||||
		        $cohorts[$c->id] = (object)[
 | 
			
		||||
                    'id' => $c->id,
 | 
			
		||||
                    'cohort' => $c,
 | 
			
		||||
                    'name' => $c->name,
 | 
			
		||||
                    'path' => local_treestudyplan_get_cohort_path($c),
 | 
			
		||||
                    'users' => $users,
 | 
			
		||||
                    ];
 | 
			
		||||
            }
 | 
			
		||||
	    }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    print $OUTPUT->header();
 | 
			
		||||
    usort($cohorts,function($a,$b){
 | 
			
		||||
        return $a->path <=> $b->path;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
     $regex = get_config('local_treestudyplan', 'cohortidregex');
 | 
			
		||||
    foreach($cohorts as $c)
 | 
			
		||||
    {
 | 
			
		||||
        $m = [];
 | 
			
		||||
		if(preg_match("/".$regex."/",$c->cohort->idnumber,$m) && $c->cohort->visible)
 | 
			
		||||
        {
 | 
			
		||||
            print "<legend class='collapse-header'><a data-toggle='collapse' class='collapsed' href='#cohort-{$c->id}' role='button'>{$c->path}</a></legend>";
 | 
			
		||||
            print "<div id='cohort-{$c->id}' class='collapse'>";
 | 
			
		||||
            print "<div class='card card-body'><ul class='gradecardlist'>";
 | 
			
		||||
            foreach($c->users as $u)
 | 
			
		||||
            {
 | 
			
		||||
                print "<li class='gradestudent'>";
 | 
			
		||||
                print "<a href='/local/treestudyplan/reports.php?studentid={$u->userid}&cohortid={$c->id}'>{$u->firstname} {$u->lastname}</a>";
 | 
			
		||||
                print "</li>";
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            print "</ul>";
 | 
			
		||||
            print "</div></div>";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    print "</div>";
 | 
			
		||||
    
 | 
			
		||||
    print $OUTPUT->footer();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								settings.php
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								settings.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,17 +1,17 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of Moodle - http://moodle.org/
 | 
			
		||||
// 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
 | 
			
		||||
// 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
 | 
			
		||||
// 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
 | 
			
		||||
// You should have received a copy of the GNU General Public License.
 | 
			
		||||
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
 *
 | 
			
		||||
 * @package    local_chronotable
 | 
			
		||||
 * @copyright  2017 Alexander Bias, Ulm University <alexander.bias@uni-ulm.de>
 | 
			
		||||
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
defined('MOODLE_INTERNAL') || die();
 | 
			
		||||
| 
						 | 
				
			
			@ -43,15 +43,15 @@ if ($hassiteconfig) {
 | 
			
		|||
			get_string('settingspage', 'local_treestudyplan', null, true));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// GOAL AGGREGATION SETTINGS
 | 
			
		||||
	// GOAL AGGREGATION SETTINGS.
 | 
			
		||||
	$page->add(new admin_setting_heading('local_treestudyplan/aggregation_heading',
 | 
			
		||||
		get_string('setting_aggregation_heading', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_aggregation_heading', 'local_treestudyplan')
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	$aggregators = [];
 | 
			
		||||
	foreach(aggregator::list() as $a){
 | 
			
		||||
		$aggregators[$a] = get_string("{$a}_aggregator_title",'local_treestudyplan', null, true);
 | 
			
		||||
	foreach (aggregator::list() as $a) {
 | 
			
		||||
		$aggregators[$a] = get_string("{$a}_aggregator_title", 'local_treestudyplan', null, true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$page->add(new admin_setting_configselect('local_treestudyplan/aggregation_mode',
 | 
			
		||||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ if ($hassiteconfig) {
 | 
			
		|||
			$aggregators
 | 
			
		||||
		));
 | 
			
		||||
 | 
			
		||||
	// DISPLAY COURSE INFO SETTINGS
 | 
			
		||||
	// DISPLAY COURSE INFO SETTINGS.
 | 
			
		||||
	$page->add(new admin_setting_heading('local_treestudyplan/display_heading',
 | 
			
		||||
		get_string('setting_display_heading', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_display_heading', 'local_treestudyplan')
 | 
			
		||||
| 
						 | 
				
			
			@ -70,9 +70,9 @@ if ($hassiteconfig) {
 | 
			
		|||
	$displayfields = ["shortname" => get_string("shortname"), "idnumber" => get_string("idnumber")];
 | 
			
		||||
	$handler = \core_customfield\handler::get_handler('core_course', 'course');
 | 
			
		||||
 | 
			
		||||
	foreach($handler->get_categories_with_fields() as $cat){
 | 
			
		||||
	foreach ($handler->get_categories_with_fields() as $cat) {
 | 
			
		||||
		$catname = $cat->get_formatted_name();
 | 
			
		||||
		foreach($cat->get_fields() as $field) {
 | 
			
		||||
		foreach ($cat->get_fields() as $field) {
 | 
			
		||||
			$fieldname = $field->get_formatted_name();
 | 
			
		||||
			$fieldid = $field->get("shortname");
 | 
			
		||||
			$displayfields["customfield_".$fieldid] = $catname.": ".$fieldname;
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +86,7 @@ if ($hassiteconfig) {
 | 
			
		|||
			$displayfields
 | 
			
		||||
		));
 | 
			
		||||
 | 
			
		||||
	// BISTATE AGGREGATON DEFAULTS
 | 
			
		||||
	// BISTATE AGGREGATON DEFAULTS.
 | 
			
		||||
	$page->add(new admin_setting_heading('local_treestudyplan/bistate_aggregation_heading',
 | 
			
		||||
		get_string('setting_bistate_heading', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_bistate_heading', 'local_treestudyplan')
 | 
			
		||||
| 
						 | 
				
			
			@ -150,48 +150,48 @@ if ($hassiteconfig) {
 | 
			
		|||
		get_string('settingspage_csync', 'local_treestudyplan', null, true));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// Description heading
 | 
			
		||||
	// Description heading.
 | 
			
		||||
	$page_csync->add(new admin_setting_heading('local_treestudyplan/csync_heading',
 | 
			
		||||
		get_string('setting_csync_heading', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_csync_heading', 'local_treestudyplan')
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// Enable/disable cohort sync
 | 
			
		||||
	// Enable/disable cohort sync.
 | 
			
		||||
	$page_csync->add(new admin_setting_configcheckbox('local_treestudyplan/csync_enable',
 | 
			
		||||
		get_string('setting_csync_enable_field', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_csync_enable_field', 'local_treestudyplan'),
 | 
			
		||||
		false
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// Enable/disable autoremove
 | 
			
		||||
	// Enable/disable autoremove.
 | 
			
		||||
	$page_csync->add(new admin_setting_configcheckbox('local_treestudyplan/csync_autoremove',
 | 
			
		||||
		get_string('setting_csync_autoremove_field', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_csync_autoremove_field', 'local_treestudyplan'),
 | 
			
		||||
		true
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// Enable/disable remembering previously added cohort syncs so they're not automatically deleted
 | 
			
		||||
	// Enable/disable remembering previously added cohort syncs so they're not automatically deleted.
 | 
			
		||||
	$page_csync->add(new admin_setting_configcheckbox('local_treestudyplan/csync_remember_manual_csync',
 | 
			
		||||
		get_string('setting_csync_remember_manual_csync_field', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_csync_remember_manual_csync_field', 'local_treestudyplan'),
 | 
			
		||||
		true
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// Enable/disable group creation
 | 
			
		||||
	// Enable/disable group creation.
 | 
			
		||||
	$page_csync->add(new admin_setting_configcheckbox('local_treestudyplan/csync_creategroup',
 | 
			
		||||
		get_string('setting_csync_creategroup_field', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_csync_creategroup_field', 'local_treestudyplan'),
 | 
			
		||||
		true
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// Sync users too yes/no?
 | 
			
		||||
	// Sync users too yes/no?.
 | 
			
		||||
	$page_csync->add(new admin_setting_configcheckbox('local_treestudyplan/csync_users',
 | 
			
		||||
		get_string('setting_csync_users_field', 'local_treestudyplan'),
 | 
			
		||||
		get_string('settingdesc_csync_users_field', 'local_treestudyplan'),
 | 
			
		||||
		true
 | 
			
		||||
	));
 | 
			
		||||
 | 
			
		||||
	// Select csync enrol role
 | 
			
		||||
	// Select csync enrol role.
 | 
			
		||||
	if (!during_initial_install()) {
 | 
			
		||||
		$options = get_default_enrol_roles(context_system::instance());
 | 
			
		||||
		$student = get_archetype_roles('student');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,182 +0,0 @@
 | 
			
		|||
<?php
 | 
			
		||||
namespace local_treestudyplan;
 | 
			
		||||
use \local_treestudyplan\local\aggregators\bistate_aggregator;
 | 
			
		||||
 | 
			
		||||
 class bistate_aggregator_test extends \basic_testcase {
 | 
			
		||||
 | 
			
		||||
    private function goalaggregation_test($configstr, $outcome, $completions,$required){
 | 
			
		||||
        $ag = new bistate_aggregator($configstr);
 | 
			
		||||
        // test aggregation with the required data
 | 
			
		||||
        $result = $ag->aggregate_binary_goals($completions,$required);
 | 
			
		||||
        // assert if the test is succesful
 | 
			
		||||
        $this->assertEquals(completion::label($outcome),completion::label($result));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private function junctionaggregation_test($configstr, $outcome, $completions){
 | 
			
		||||
        $ag = new bistate_aggregator($configstr);
 | 
			
		||||
        // test aggregation with the minimum required data 
 | 
			
		||||
        $result = $ag->aggregate_junction($completions);
 | 
			
		||||
        // assert if the test is succesful
 | 
			
		||||
        $this->assertEquals(completion::label($outcome),completion::label($result));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_0() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::INCOMPLETE,
 | 
			
		||||
            [
 | 
			
		||||
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_1() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::FAILED,
 | 
			
		||||
            [ // completions
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
            ],
 | 
			
		||||
            [] // required
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_2() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::EXCELLENT,
 | 
			
		||||
            [
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_3() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::INCOMPLETE,
 | 
			
		||||
            [
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_4() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::PROGRESS,
 | 
			
		||||
            [
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_5() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::COMPLETED,
 | 
			
		||||
            [
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::COMPLETED,
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_goalaggregation_6() {
 | 
			
		||||
        $this->goalaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::PROGRESS,
 | 
			
		||||
            [
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
            ],
 | 
			
		||||
            [],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function test_junctionaggregation_0() {
 | 
			
		||||
        $this->junctionaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::FAILED,
 | 
			
		||||
            [
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_junctionaggregation_1() {
 | 
			
		||||
        $this->junctionaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::INCOMPLETE,
 | 
			
		||||
            [
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function test_junctionaggregation_2() {
 | 
			
		||||
        $this->junctionaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::FAILED,
 | 
			
		||||
            [
 | 
			
		||||
                completion::FAILED,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function test_junctionaggregation_3() {
 | 
			
		||||
        $this->junctionaggregation_test(
 | 
			
		||||
            '{"thresh_excellent":100,"thresh_good":85,"thresh_completed":66,"thresh_progress":25,"use_failed":true,"accept_pending_as_submitted":true}',
 | 
			
		||||
            completion::PROGRESS,
 | 
			
		||||
            [
 | 
			
		||||
                completion::PROGRESS,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
                completion::INCOMPLETE,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								version.php
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								version.php
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,29 @@
 | 
			
		|||
<?php
 | 
			
		||||
$plugin->component = 'local_treestudyplan';  // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494)
 | 
			
		||||
$plugin->version = 2023082101;  // YYYYMMDDHH (year, month, day, iteration)
 | 
			
		||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11)
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
defined('MOODLE_INTERNAL') || die();
 | 
			
		||||
 | 
			
		||||
$plugin->component = 'local_treestudyplan';  // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
 | 
			
		||||
$plugin->version = 2023082101;  // YYYYMMDDHH (year, month, day, iteration).
 | 
			
		||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
 | 
			
		||||
 | 
			
		||||
$plugin->dependencies = [
 | 
			
		||||
    'theme_boost' => 2019052000,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,25 @@
 | 
			
		|||
<?php
 | 
			
		||||
// This file is part of the Studyplan plugin for Moodle
 | 
			
		||||
//
 | 
			
		||||
// 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 <https://www.gnu.org/licenses/>.
 | 
			
		||||
/**
 | 
			
		||||
 *
 | 
			
		||||
 * @package    local_treestudyplan
 | 
			
		||||
 * @copyright  2023 P.M. Kuipers
 | 
			
		||||
 * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
require_once("../../config.php");
 | 
			
		||||
 | 
			
		||||
use \local_treestudyplan\courseservice;
 | 
			
		||||
| 
						 | 
				
			
			@ -7,20 +28,19 @@ require_once($CFG->libdir.'/weblib.php');
 | 
			
		|||
 | 
			
		||||
$systemcontext = context_system::instance();
 | 
			
		||||
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/view-plan.php",array());
 | 
			
		||||
$PAGE->set_url("/local/treestudyplan/view-plan.php", array());
 | 
			
		||||
require_login();
 | 
			
		||||
 | 
			
		||||
// Figure out the context (category or system, based on either category or context parameter)
 | 
			
		||||
$categoryid = optional_param('categoryid', 0, PARAM_INT); // Category id
 | 
			
		||||
$contextid = optional_param('contextid', 0, PARAM_INT); // Context id
 | 
			
		||||
if($categoryid > 0){
 | 
			
		||||
// Figure out the context (category or system, based on either category or context parameter).
 | 
			
		||||
$categoryid = optional_param('categoryid', 0, PARAM_INT); // Category id.
 | 
			
		||||
$contextid = optional_param('contextid', 0, PARAM_INT); // Context id.
 | 
			
		||||
if ($categoryid > 0) {
 | 
			
		||||
	$studyplancontext = context_coursecat::instance($categoryid);
 | 
			
		||||
	
 | 
			
		||||
}
 | 
			
		||||
elseif($contextid > 0)
 | 
			
		||||
{
 | 
			
		||||
else if ($contextid > 0) {
 | 
			
		||||
	$studyplancontext = context::instance_by_id($contextid);
 | 
			
		||||
	if(in_array($studyplancontext->contextlevel,[CONTEXT_SYSTEM,CONTEXT_COURSECAT]))
 | 
			
		||||
	if (in_array($studyplancontext->contextlevel, [CONTEXT_SYSTEM, CONTEXT_COURSECAT]))
 | 
			
		||||
	{
 | 
			
		||||
		$categoryid = $studyplancontext->instanceid;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,42 +51,42 @@ elseif($contextid > 0)
 | 
			
		|||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
	// If no context is selected, find the first available one
 | 
			
		||||
	// If no context is selected, find the first available one.
 | 
			
		||||
	$available_contexts = courseservice::list_accessible_categories_with_usage("view");
 | 
			
		||||
	$contextid=1; // fallback to system context
 | 
			
		||||
	foreach($available_contexts as $ctx){
 | 
			
		||||
		if($ctx->count > 0){
 | 
			
		||||
	$contextid=1; // fallback to system context.
 | 
			
		||||
	foreach ($available_contexts as $ctx) {
 | 
			
		||||
		if ($ctx->count > 0) {
 | 
			
		||||
			$contextid = $ctx->ctxid;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// reload page with selected category
 | 
			
		||||
	$url = new \moodle_url('/local/treestudyplan/view-plan.php',["contextid" => $contextid]);
 | 
			
		||||
	// reload page with selected category.
 | 
			
		||||
	$url = new \moodle_url('/local/treestudyplan/view-plan.php', ["contextid" => $contextid]);
 | 
			
		||||
	header('Location: '.$url->out(false), true, 302);
 | 
			
		||||
	exit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
require_capability('local/treestudyplan:viewuserreports',$studyplancontext);
 | 
			
		||||
$contextname = $studyplancontext->get_context_name(false,false);
 | 
			
		||||
require_capability('local/treestudyplan:viewuserreports', $studyplancontext);
 | 
			
		||||
$contextname = $studyplancontext->get_context_name(false, false);
 | 
			
		||||
 | 
			
		||||
$PAGE->set_pagelayout('base');
 | 
			
		||||
$PAGE->set_context($studyplancontext);
 | 
			
		||||
$PAGE->set_title(get_string('view_plan','local_treestudyplan')." - ".$contextname);
 | 
			
		||||
$PAGE->set_heading(get_string('view_plan','local_treestudyplan')." - ".$contextname);
 | 
			
		||||
$PAGE->set_title(get_string('view_plan', 'local_treestudyplan')." - ".$contextname);
 | 
			
		||||
$PAGE->set_heading(get_string('view_plan', 'local_treestudyplan')." - ".$contextname);
 | 
			
		||||
 | 
			
		||||
if($studyplancontext->id > 1){
 | 
			
		||||
if ($studyplancontext->id > 1) {
 | 
			
		||||
	navigation_node::override_active_url(new moodle_url('/course/index.php', ['categoryid' => $categoryid ]));
 | 
			
		||||
	$PAGE->navbar->add(get_string('view_plan','local_treestudyplan'));
 | 
			
		||||
	$PAGE->navbar->add(get_string('view_plan', 'local_treestudyplan'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Load javascripts and specific css
 | 
			
		||||
// Load javascripts and specific css.
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css'));
 | 
			
		||||
$PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css'));
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-view-plan', 'init',[$studyplancontext->id,$categoryid]);
 | 
			
		||||
$PAGE->requires->js_call_amd('local_treestudyplan/page-view-plan', 'init', [$studyplancontext->id, $categoryid]);
 | 
			
		||||
 | 
			
		||||
//Local translate function
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan'){
 | 
			
		||||
	print get_string($str,$plugin,$param);
 | 
			
		||||
//Local translate function.
 | 
			
		||||
function t($str, $param=null, $plugin='local_treestudyplan') {
 | 
			
		||||
	print get_string($str, $plugin, $param);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print $OUTPUT->header();
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +102,7 @@ print $OUTPUT->header();
 | 
			
		|||
			<b-form-select text='<?php print($contextname);?>' :value="contextid">
 | 
			
		||||
				<b-form-select-option v-for='ctx in usedcontexts' :key='ctx.id' :value="ctx.context_id" @click='switchContext(ctx)'
 | 
			
		||||
					:active="ctx.context_id == contextid" :class="(ctx.studyplancount > 0)?'font-weight-bold':''"
 | 
			
		||||
				><span v-for="(p,i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</b-form-select-option>
 | 
			
		||||
				><span v-for="(p, i) in ctx.category.path"><span v-if="i>0"> / </span>{{ p }}</span> <span>({{ ctx.studyplancount }})</b-form-select-option>
 | 
			
		||||
			</b-form-select>
 | 
			
		||||
		</div>
 | 
			
		||||
		<h3 v-else><?php print $contextname; ?></h3>
 | 
			
		||||
| 
						 | 
				
			
			@ -90,7 +110,7 @@ print $OUTPUT->header();
 | 
			
		|||
			<a href='#' v-if='displayedstudyplan' @click.prevent='closeStudyplan'><i style='font-size: 150%;' class='fa fa-chevron-left'></i> <?php t('back');?></a>
 | 
			
		||||
			<span v-if='displayedstudyplan'><?php t("studyplan_select"); ?></span> 
 | 
			
		||||
			<b-form-select v-if='displayedstudyplan' lazy :text='dropdown_title'>
 | 
			
		||||
				<b-form-select-option v-for='(studyplan,planindex) in studyplans' :key='studyplan.id' @click='selectStudyplan(studyplan)'>{{ studyplan.name }}</b-form-select-option>
 | 
			
		||||
				<b-form-select-option v-for='(studyplan, planindex) in studyplans' :key='studyplan.id' @click='selectStudyplan(studyplan)'>{{ studyplan.name }}</b-form-select-option>
 | 
			
		||||
			</b-form-select> 
 | 
			
		||||
			<b-button variant='primary' v-if='associatedstudents && associatedstudents.length > 0' v-b-toggle.toolbox-sidebar><?php t('selectstudent_btn') ?></b-button>
 | 
			
		||||
		</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +125,7 @@ print $OUTPUT->header();
 | 
			
		|||
				<p><?php t("studyplan_noneselected"); ?></p>
 | 
			
		||||
				<b-card-group deck>
 | 
			
		||||
					<s-studyplan-card
 | 
			
		||||
						v-for='(studyplan,planindex) in studyplans' 
 | 
			
		||||
						v-for='(studyplan, planindex) in studyplans'
 | 
			
		||||
						:key='studyplan.id'
 | 
			
		||||
						v-model='studyplans[planindex]'
 | 
			
		||||
						open
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Reference in a new issue